環境
$ rbenv version 2.7.0
お試しコード
class User attr_accessor :name def initialize(name) @name = name end # 引数のいらないメソッド def print_name p @name end # 引数のいるメソッド def greet(greet) p greet end end users = [User.new('neko314'), User.new('keiko')] # => [#<User:0x00007fe9b10c9778 @name="neko314">, #<User:0x00007fe9b10c9728 @name="keiko">]
引数不要のメソッドの場合
> users.each(&:print_name) "neko314" "keiko"
引数が必要なメソッドの場合
irb(main):020:1> users.each(&:greet("hello")) irb(main):021:1> irb(main):022:1> irb(main):023:1> irb(main):024:1> irb(main):025:1> irb(main):026:1> irb(main):027:1> irb(main):028:1> irb(main):029:1> (終わらない)
2.6.5でやってみるとsyntaxエラーになりました
> users.each(&:greet("hello")) Traceback (most recent call last): 3: from /Users/keiko/.rbenv/versions/2.6.5/bin/irb:23:in `<main>' 2: from /Users/keiko/.rbenv/versions/2.6.5/bin/irb:23:in `load' 1: from /Users/keiko/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/irb-1.0.0/exe/irb:11:in `<top (required)>' SyntaxError ((irb):21: syntax error, unexpected '(', expecting ')') users.each(&:greet("hello")) ^ (irb):21: syntax error, unexpected ')', expecting end-of-input users.each(&:greet("hello")) ^
そもそも &:method
ってなんなのか
2015年なのでちょっと古いけどUnderstanding Ruby's idiom: array.map(&:method)がよかったです。たぶん今でもこれなんじゃないかな。もし新しいのと差分があっても後で差分を追えばいいくらいの感じな気がします。
この記事中ににありました。
[].map(&MyClass.new) # => trying to convert to a proc
私「あ!&:method
の &
ってブロック引数の&
で:method
はシンボルなのか!!」
と気付きました。
なんとこれまで私はこの記法に対して思考を停止していたので&:
でワンセットのように思っていました。
また
What Symbol#to_proc does is quite clever. It tries to calls a method with the same name (in our example, method_name) on the given object.
:upcase.to_proc.call("string") # => STRING
という説明とサンプルがありました!これがかなりしっくり来ました。
なので、上述の私のサンプルコードで試してみても同じように実行できます。
> :print_name.to_proc.call(User.new('neko')) "neko"
すごい!!
これを踏まえると、 [Ruby] ブロック変数とブロック引数とProc引数の違い|TechRacho(テックラッチョ)〜エンジニアの「?」を「!」に〜|BPS株式会社 の中で紹介されているように、&はじまりの引数はプロック引数と呼ぶほうがなんだか正体がわかように思います。
シンタックスエラーの正体
シンボルの文法を再確認してみました。
class Symbol (Ruby 2.7.0 リファレンスマニュアル)
:' の後には識別子、メソッド名(
!',?',
=' などの接尾辞を含む)、変数名 (`$'などの接頭辞を含む)、再定義できる演算子のいずれかに適合するものしか書くことはできません(そうでなければ文法エラーになります)
これを踏まえて、先程シンタックスエラーになったコードを見返してみます。
users.each(&:greet("hello")) Traceback (most recent call last): 3: from /Users/keiko/.rbenv/versions/2.6.5/bin/irb:23:in `<main>' 2: from /Users/keiko/.rbenv/versions/2.6.5/bin/irb:23:in `load' 1: from /Users/keiko/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/irb-1.0.0/exe/irb:11:in `<top (required)>' SyntaxError ((irb):21: syntax error, unexpected '(', expecting ')') users.each(&:greet("hello")) ^ (irb):21: syntax error, unexpected ')', expecting end-of-input users.each(&:greet("hello")) ^
このシンタックスエラーはunexpected ')', expecting end-of-input
とはありますがシンボルの部分のシンタックスエラーのような気がしてます。でも不確か。
まとめ
完璧とはいかなくても&:method
がちょっと理解できたと思います。&:
不思議な独自記法と思ってたときは:&
と書いたりしてましたが、意味がわかったのでそれもなくせそうです。