Rubyで "&" を使うと幸せになれるらしいよ (*´Д`)ノ
という記事を読んでいても力不足のためかいまいちピンと来なかったので、自分なりに理解できるように挙動を書いていこうと思います。以下のコードは全て元記事から引用したものであって僕のオリジナルではありません。
#1
3.times do
puts 'hogehoge'
end
proc = Proc.new { puts 'hogehoge' }
#2
3.times do
proc.call
end
#3
3.times(&proc)
この辺の挙動は一目瞭然です。proc
オブジェクト内で定義した変数や機能などをあとで使いたいときに&proc
で呼び出せる。つまりコードの節約になるってこと、だと思います。
def sample_mb(&hoge)
hoge.call "hoge", "fuga"
end
sample_mb {|a, b| p [a, b]} # ["hoge", "fuga"]
続いてこちら。予めsample_mb
という関数を定義した後arg = {|a, b| p [a, b]}
でその関数を呼んでいます。「ん?でもここで&hoge
っていつどこで定義されてるの?」となってしまったので調べてみることに。
202: def sample_mb(&hoge)
=> 203: binding.pry
204: hoge.call "hoge", "huga"
205: end
[1] pry(#<Module>)> hoge
=> #<Proc:0x007fff49471238@/Users/hironorisama/Projects/github/gakkimania/lib/tasks/migrate.rake:207>
[2] pry(#<Module>)> hoge.call "hoge"
["hoge", nil]
=> ["hoge", nil]
[3] pry(#<Module>)> hoge.call "hoge", "hogehoge", "hogehogehoge"
["hoge", "hogehoge"]
=> ["hoge", "hogehoge"]
sample_mb
を呼んだ瞬間に[a,b]
として配列が用意されるのかと思っていたらhoge
自体はproc
オブジェクトとして生成されるだけであってhoge.call
で箱に中身が入って初めて配列となって姿を表した、という実感。中身の数が宣言した数より少ない場合はnil
に多い場合はシカト。つまりエラーを出さないという面では使い方によっては良いことなのかもしれません。ちなみにこれはprocをインスタンスとして使うlambda
では起きない挙動です。
proc # returns '#<Proc:0x007f96b1032d30@(irb):75>'
lam # returns '<Proc:0x007f96b1b41938@(irb):76 (lambda)>'
lam = lambda { |x| puts x } # creates a lambda that takes 1 argument
lam.call(2) # prints out 2
lam.call # ArgumentError: wrong number of arguments (0 for 1)
lam.call(1,2,3) # ArgumentError: wrong number of arguments (3 for 1)
# In contrast, procs don’t care if they are passed the wrong number of arguments.
proc = Proc.new { |x| puts x } # creates a proc that takes 1 argument
proc.call(2) # prints out 2
proc.call # returns nil
proc.call(1,2,3) # prints out 1 and forgets about the extra arguments
procオブジェクトについて
proc
オブジェクトはこちらとこの辺の記事より簡単な定義を確認しました。
proc = Proc.new { puts "Hello world" }
proc.class # returns 'Proc'
proc.call "hogehoge"
ActiveRecord
etcと同じくproc
内にも独自に定義されたメソッドがありcall
もその中の一つ。つまり配列に対してarray.call
をすると
[8] pry(#<Module>)> def try_call_on_array()
[8] pry(#<Module>)* array = []
[8] pry(#<Module>)* array.call "hoge"
[8] pry(#<Module>)* array
[8] pry(#<Module>)* end
=> :try_call_on_array
[9] pry(#<Module>)> try_call_on_array()
NoMethodError: undefined method `call' for []:Array
「そんなメソッド定義されてないよ!」と怒られてしまう。
他にも
[21] pry(#<Module>)> def proc_test
[21] pry(#<Module>)* proc = Proc.new{ return }
[21] pry(#<Module>)* puts "fuga"
[21] pry(#<Module>)* proc.call
[21] pry(#<Module>)* end
=> :proc_test
[22] pry(#<Module>)> proc_test
fuga
=> nil
[18] pry(#<Module>)> def proc_test
[18] pry(#<Module>)* proc = Proc.new{ return }
[18] pry(#<Module>)* puts "hello world"
[18] pry(#<Module>)* lam.call
[18] pry(#<Module>)* end
=> :proc_test
[19] pry(#<Module>)> lambda_test
hoge
=> nil
は同じ挙動を示すのに対し
[12] pry(#<Module>)> def lambda_test
[12] pry(#<Module>)* lam = lambda { return }
[12] pry(#<Module>)* lam.call
[12] pry(#<Module>)* puts "Hello world"
[12] pry(#<Module>)* end
=> :lambda_test
[13] pry(#<Module>)> lambda_test
Hello world
=> nil
[14] pry(#<Module>)> def proc_test
[14] pry(#<Module>)* proc = Proc.new { return }
[14] pry(#<Module>)* proc.call
[14] pry(#<Module>)* puts "Hello world"
[14] pry(#<Module>)* end
=> :proc_test
[15] pry(#<Module>)>
[16] pry(#<Module>)> proc_test
=> nil
proc
はreturn
処理を行なってすぐcall
を行った後にbreak
してしまうのに対し、lambda
はその後も最後の行まで処理を行ってくれます。lambda
とproc
の違いについてはこの記事も参考になります。
(時系列で書いているので)話がだいぶそれましたが、プラクティカルな面では以下のように使えると元記事がおっしゃっていました。
require 'rubygems'
require 'active_support'
class Sample
attr_accessor :id
def initialize
@id = Time.now.to_i
end
end
data1 = Sample.new
data2 = Sample.new
data3 = Sample.new
@data = [data1, data2, data3]
p @data.map {|data| data.id} # [1264564410, 1264564410, 1264564410]
p @data.map(&:id) # [1264564410, 1264564410, 1264564410]
投稿者曰く「["aaa", "bbbb", "cc"].map(&:length)
と["aaa", "bbbb", "cc"].map {|word| word.length}
」は同じことを意味していると。明らかにスッキリしています。