ここでの&はビット演算子ではなくProc関連で出てくるアレのことです。
知ってる人にとっては当たり前のことかもしれませんが、自分的にわかりやすい(それでいて他にこういう書き方をしてる記事がなかった)ので書き留めておきます。
そもそも&と言えば
ブロックをProcオブジェクトに変換する記法です。最も単純な例で
def test(&fun)
fun.call
end
test { p 1 } #=> 1
これは1と表示されます。
・ブロック { p 1 } がfunに渡される
・このfunは&によってProcオブジェクトに変換される。
・そのため、funはProcのメソッドの1つであるcallを用いた呼び出しを受け付けるようになる
という算段ですね。
ちなみに本題からは外れますが、Rubyでは
・「yieldとは&引数で生成されたProcを.callする略記法である」
・「yieldに渡す&引数Procはメソッド定義行で省略して構わない」
と定められていますので、上記は
def test
yield
end
test { p 1 } #=> 1
でもOKです。しんぷる!
では、次は?
def test(&fun)
fun.call
end
proc = Proc.new { p 1 }
test &proc #=> 1
これも1と表示されます。
ここで最初私は、
「(この部分は間違いです)&はProcへの変換のおまじないだから、もともとProcのものに&をつけても相変わらずProcのままで、&procでも&funでもProcオブジェクトが渡されてるのかな」
と思いました。
上記が正しければ、片方の&を取り払っても問題ないはずですね。ずっとProcが渡されているはずですから。
実際にやってみると…
def test(fun)
fun.call
end
proc = Proc.new { p 1 }
test &proc
#=> wrong number of arguments(given 0, expected 1)
あれ?エラーですね。
何が起こったのでしょう。試しに&procのクラスを調べてみましょうか。
proc = Proc.new { p 1 }
p proc.class #=> Proc
# p (&proc).class #=> syntax Error
どうやらprocと&procは別物。&procは.classそのものを受け付けないようです。
それもそのはず。実は、&procは__オブジェクトですらない__からです。
Ruby公式ドキュメントには、
ブロック付きメソッドに対して Proc オブジェクトを `&' を指定して渡すと
呼び出しブロックのように動作します。
と、ちゃんと書かれておりました。そして、Rubyでブロックはオブジェクトではありません(逆に言えば、ブロックをオブジェクトとして扱いたいという欲求から出てきたのがProcなのだと思います)。
こうして、&には__二つの意味__があることがわかります。
・ブロックからProcオブジェクトへの変換
・Procオブジェクトからブロックへの変換
真逆ですね。
うーん…正直奇妙に思えましたが、慣れるとこれがむしろしっくりくるのでしょうか。
余談
上でエラーになったコードですが、そもそも&を使わずにProc状態でどんどん受け渡せば問題なく動きます。
def test(fun)
fun.call
end
proc = Proc.new { p 1 }
test proc #=> 1
随分すっきりしました。
&を使わない引数なら、複数のProcも問題なく受け渡せます(&付き仮引数の場合はメソッドにつき1つだけと決められている)。
def test(one, two)
one.call
two.call
end
proc1 = Proc.new { p 1 }
proc2 = Proc.new { p 2 }
test proc1, proc2
#=> 1
# 2
こうしてみると、仕様上は、Procでないブロックを定義せずとも十分言語として機能するということになりそうです。
しかし、やっぱりRubyっぽくmapなどには直接ブロックを渡したいですし、先に述べたyield記法は捨てがたい。とすると可読性の上で&は大事ですので、コイツとは末永く付き合っていくことになりそうだな、と思ったのでした。
そこで付き合うコツとして、&の意味が文脈で変わってくるので、(これはすべての型やクラスに言えることかとは思いますが)プログラムの中で「これは今Procとブロックのどちらなのか」を常に把握するのが大事なのかな、というのが今の考えです。
参考
大変参考になりました。ありがとうございました。
Ruby block/proc/lambdaの使いどころ
Procを制する者がRubyを制す(嘘)