ホームページ制作・大阪インフォメーションメディアデザイン

06-6809-5021

Staff Blog
スタッフブログ

  • Twitterでシェア
  • このエントリーをはてなブックマークに追加
  • Google+でシェア
  • follow us in feedly

RubyのArray#eachをmap, reduceに置き換える

diamond-704072_1000

Rubyを使いはじめて1年ほど経ちましたが、やっとRubyの便利さが分かってきたかなあというレベルです。今回は、最近便利に使っているArrayクラスのメソッドを使った、便利な書き方についてご紹介したいと思います。

Array#each

Array#each

配列の各要素に対して何かしらの処理を行いたいということは頻繁に発生します。
そんなとき、他の言語だと foreach などの制御構造を使って書くところですが、Rubyでは Array#each で書けます。

3つのサイコロを振って、出た目の合計を出力するプログラムは以下のように書けます。

#!/usr/bin/env ruby

class Dice
  attr_reader :result

  def shake
    @result = Random.new.rand(1..6)
  end 
end

# サイコロを3つ生成
dices = []
([Dice] * 3).each do |dice_class|
  dices << dice_class.new
end

# 各サイコロをふって結果を得る
results = []
dices.each do |dice|
  results << dice.shake
end

# 合計を計算
total = 0 
results.each do |result|
  total += result
end

puts total

foreach などの制御構造を使った書き方よりも直感的ですよね。
でも、ちょっと each 使いすぎな感があります(・_・;)

Arrayクラスには他にも便利なメソッドが用意されていて、もっと簡単に書くことができます。

Enumerable#map

Enumerable#map

ArrayクラスにインクルードされているEnumerableモジュールにmapというメソッドが定義されています。

このメソッドは配列の各要素に対して任意の処理を実行して、その結果の配列を返してくれます。配列の各要素を別のものに変換するのに便利です。

これを使うと、上記のプログラムは以下のように書き換えられます。

# サイコロを3つ生成
# dices = []
# ([Dice] * 3).each do |dice_class|
#   dices << dice_class.new
# end
#  ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓
#
# Diceクラスの配列をDiceオブジェクトの配列に変換する
dices = ([Dice] * 3).map{ |dice_class| dice_class.new }

# 各サイコロをふって結果を得る
# results = dices.map{ |dice| dice.shake }
# results = []
# dices.each do |dice|
#   results << dice.shake
# end
#  ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓
#
# Diceオブジェクトの配列をサイコロを振って出た目の配列に変換する
results = dices.map{ |dice| dice.shake }

かなりシンプルになりました。
さらに、今回のようにmapの中の処理内容が、対象オブジェクトのメソッドを実行するだけの場合は以下のようにも書けます。


# サイコロを3つ生成
dices = ([Dice] * 3).map(&:new)

# 各サイコロをふって結果を得る
results = dices.map(&:shake)

このカラクリは以下のページで分かりやすく解説されています。

Rubyで "&" を使うと幸せになれるらしいよ (*´Д`)ノ - (゚∀゚)o彡 sasata299's blog

mapを使うことで最初のプログラムは以下のようにスッキリしました。

#!/usr/bin/env ruby

class Dice
  attr_reader :result

  def shake
    @result = Random.new.rand(1..6)
  end 
end

# サイコロを3つ生成
dices = ([Dice] * 3).map(&:new)

# 各サイコロをふって結果を得る
results = dices.map(&:shake)

# 合計を計算
total = 0 
results.each do |result|
  total += result
end

puts total

Enumerable#reduce

Enumerable#reduce

Enumerableモジュールのreduceメソッドは配列の各要素の足し込み計算に使えます。
これを使って合計計算の部分を書き換えてみます。

# 合計を計算
# total = 0
# results.each do |result|
#   total += result
# end
#  ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓
#
total = results.reduce(0){ |total, result| total + result }

reduceの引数に指定している 0 が初期値、ブロックの引数 result が配列の各要素、total がそれまでに処理した合計値です。

これで、はじめのプログラムは以下のようになりました。

#!/usr/bin/env ruby

class Dice
  attr_reader :result

  def shake
    @result = Random.new.rand(1..6)
  end 
end

# サイコロを3つ生成
dices = ([Dice] * 3).map(&:new)

# 各サイコロをふって結果を得る
results = dices.map(&:shake)

# 合計を計算
total = results.reduce(0){ |total, result| total + result }

puts total

まとめ

結果的に each を使わずに同じ処理が記述できました。
Rubyはコアライブラリに便利機能がたくさんあるので、それらを有効活用して読みやすいソースコードを書きたいですね。

ちなみに、メソッドチェインすれば今回のプログラムはさらに短く書けます。

#!/usr/bin/env ruby

class Dice
  attr_reader :result

  def shake
    @result = Random.new.rand(1..6)
  end 
end

puts ([Dice] * 3)
      .map(&:new)
      .map(&:shake)
      .reduce(0){ |total, result| total + result }

ただ、やり過ぎるとデバッグがやり辛くなるので、ほどほどに。

今回は以上です。

この記事を共有する

投稿者:i.M.D. Staff
    2015年05月27日水曜日 | 13:41
    Rubyはライブラリが充実してて便利

    この人が書いたその他のブログ記事

    コメントを残す

    メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

    ranking 人気記事ランキング

    • I.M.Dコーポレートサイト
    お問い合わせ