ruby: &とprocオブジェクト学習メモ

<a href="http://blog.livedoor.jp/sasata299/archives/51382454.html" target="_blank">Rubyで "&" を使うと幸せになれるらしいよ (*´Д`)ﾉ </a>

という記事を読んでいても力不足のためかいまいちピンと来なかったので、自分なりに理解できるように挙動を書いていこうと思います。以下のコードは全て元記事から引用したものであって僕のオリジナルではありません。

```ex.rb
#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`っていつどこで定義されてるの？」となってしまったので調べてみることに。

```irb
    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`では起きない挙動です。

```irb
proc   # returns '#<Proc:0x007f96b1032d30@(irb):75>'
lam    # returns '<Proc:0x007f96b1b41938@(irb):76 (lambda)>'
```

```test.rb
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`オブジェクトは[こちら](http://awaxman11.github.io/blog/2013/08/05/what-is-the-difference-between-a-block/)と<a href="https://docs.ruby-lang.org/ja/latest/doc/spec=2fcall.html#block" target="_blank">この辺の</a>記事より簡単な定義を確認しました。

```
proc = Proc.new { puts "Hello world" }
proc.class # returns 'Proc'
proc.call "hogehoge"
```

`ActiveRecord`etcと同じく`proc`内にも独自に定義されたメソッドがあり`call`もその中の一つ。つまり配列に対して`array.call`をすると

```irb
[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
```

「そんなメソッド定義されてないよ！」と怒られてしまう。

他にも

```irb
[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
```

は同じ挙動を示すのに対し

```irb
[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`の違いについては[この記事](http://qiita.com/ryo-ma/items/24c46592b45775e8644d)も参考になります。

（時系列で書いているので）話がだいぶそれましたが、プラクティカルな面では以下のように使えると元記事がおっしゃっていました。

```test.rb
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}`」は同じことを意味していると。明らかにスッキリしています。