このコードは a RailsCast にあります。
def tag_names
@tag_names || tags.map(&:name).join(' ')
end
map(&:name)
の(&:name)
はどういう意味ですか?
tags.map(&:name.to_proc).join(' ')
の省略形です
foo
がto_proc
メソッドを持つオブジェクトである場合、それを&foo
としてメソッドに渡すことができます。これはfoo.to_proc
を呼び出し、それをメソッドのブロックとして使用します。
Symbol#to_proc
メソッドはもともとActiveSupportによって追加されましたが、Ruby 1.8.7に統合されました。これがその実装です。
class Symbol
def to_proc
Proc.new do |obj, *args|
obj.send self, *args
end
end
end
多くの人に知られていない、もう一つのクールな速記は、
array.each(&method(:foo))
これはの省略形です
array.each { |element| foo(element) }
method(:foo)
を呼び出すことによって、そのMethod
メソッドを表すself
からfoo
オブジェクトを取得し、それをProc
に変換する&
method があることを示すためにto_proc
を使用しました。
ポイントフリースタイルを使いたいときにとても便利です。例として、文字列"foo"
と等しい文字列が配列内にあるかどうかを確認することがあります。従来の方法があります。
["bar", "baz", "foo"].any? { |str| str == "foo" }
そして、ポイントフリーの方法があります。
["bar", "baz", "foo"].any?(&"foo".method(:==))
好ましい方法は、最も読みやすい方法です。
それと同等です
def tag_names
@tag_names || tags.map { |tag| tag.name }.join(' ')
end
また、アンパーサンドの#to_proc
マジックはSymbolだけでなく、どのクラスでも使えることに注意しましょう。多くのRubyistsは、Arrayクラスに#to_proc
を定義することを選択します。
class Array
def to_proc
proc { |receiver| receiver.send *self }
end
end
# And then...
[ 'Hello', 'Goodbye' ].map &[ :+, ' world!' ]
#=> ["Hello world!", "Goodbye world!"]
Ampersand &
は、そのオペランドにto_proc
メッセージを送信することによって機能します。上記のコードでは、これはArrayクラスです。そして、私は#to_proc
メソッドをArrayに定義したので、その行は次のようになります。
[ 'Hello', 'Goodbye' ].map { |receiver| receiver.send( :+, ' world!' ) }
tags.map { |tag| tag.name }.join(' ')
の省略形です
tags.map(&:name)
と同じ
tags.map{|tag| tag.name}
&:name
は、呼び出されるメソッド名としてシンボルを使用するだけです。
Josh Leeの答えはほぼ同等ですが、同等のRubyコードは次のようになっているはずです。
class Symbol
def to_proc
Proc.new do |receiver|
receiver.send self
end
end
end
ではない
class Symbol
def to_proc
Proc.new do |obj, *args|
obj.send self, *args
end
end
end
このコードでは、print [[1,'a'],[2,'b'],[3,'c']].map(&:first)
が実行されると、最初の入力[1,'a']
を1と 'a'に分割してobj
1とargs*
'aを指定し、Fixnumオブジェクト1にはselfというメソッドがないため、エラーが発生します。 。
[[1,'a'],[2,'b'],[3,'c']].map(&:first)
が実行されたとき。
:first
はSymbolオブジェクトなので、&:first
がパラメータとしてmapメソッドに渡されると、Symbol#to_procが呼び出されます。
mapは呼び出しメッセージを:first.to_procにパラメータ[1,'a']
で送信します、例えば:first.to_proc.call([1,'a'])
が実行されます。
symbolクラスのto_procプロシージャは、パラメータ(:first)を指定して配列オブジェクト([1,'a']
)に送信メッセージを送信します。たとえば、[1,'a'].send(:first)
が実行されます。
[[1,'a'],[2,'b'],[3,'c']]
オブジェクトの残りの要素を繰り返し処理します。
これは[[1,'a'],[2,'b'],[3,'c']].map(|e| e.first)
式を実行するのと同じです。
ここで2つのことが起こっています、そして両方を理解することは重要です。
他の回答で説明されているように、Symbol#to_proc
メソッドが呼び出されています。
しかし、シンボルに対してto_proc
が呼び出されるのは、ブロック引数としてmap
に渡されるためです。メソッド呼び出しの引数の前に&
を配置すると、このように渡されます。これは、シンボル付きのmap
だけでなく、あらゆるRubyメソッドに当てはまります。
def some_method(*args, &block)
puts "args: #{args.inspect}"
puts "block: #{block.inspect}"
end
some_method(:whatever)
# args: [:whatever]
# block: nil
some_method(&:whatever)
# args: []
# block: #<Proc:0x007fd23d010da8>
some_method(&"whatever")
# TypeError: wrong argument type String (expected Proc)
# (String doesn't respond to #to_proc)
Symbol
はブロックとして渡されるため、Proc
に変換されます。アンパサンドなしでprocを.map
に渡すことでこれを示すことができます。
arr = %w(Apple banana)
reverse_upcase = proc { |i| i.reverse.upcase }
reverse_upcase.is_a?(Proc)
=> true
arr.map(reverse_upcase)
# ArgumentError: wrong number of arguments (1 for 0)
# (map expects 0 positional arguments and one block argument)
arr.map(&reverse_upcase)
=> ["ELPPA", "ANANAB"]
変換する必要はありませんが、メソッドはブロック引数を必要とするため、使用方法がわかりません。 &
を付けて渡すと、.map
に期待するブロックが与えられます。
(&:name)は(&:name.to_proc)の省略形です。tags.map{ |t| t.name }.join(' ')
と同じです。
to_procは実際にはCで実装されています
map(&:name)は列挙可能なオブジェクト(あなたの場合はタグ)を取り、各要素/タグに対してnameメソッドを実行し、そのメソッドから返されたそれぞれの値を出力します。
それはの省略形です
array.map { |element| element.name }
これは要素(タグ)名の配列を返します
初心者の視点から見て、私たちはすでにすばらしい答えを持っていますが、私は追加情報を加えたいと思います:
Rubyでmap(&:name)はどういう意味ですか?
これはつまり、マップ関数のパラメータとして別のメソッドを渡しているということです。 (実際には、procに変換されるシンボルを渡します。ただし、この場合はこれは重要ではありません)。
重要なことは、method
という名前のname
があり、これが従来のblock
スタイルの代わりに引数としてmapメソッドによって使用されることです。
ここで:name
はtagオブジェクトのメソッドname
を指すシンボルです。 &:name
をmap
に渡すと、name
をprocオブジェクトとして扱います。手短に言うと、tags.map(&:name)
は次のように動作します。
tags.map do |tag|
tag.name
end
その意味は
array.each(&:to_sym.to_proc)
以下と同じです。
def tag_names
if @tag_names
@tag_names
else
tags.map{ |t| t.name }.join(' ')
end
基本的に、配列内の各タグに対してメソッド呼び出しtag.name
を実行します。
これは単純化されたRubyの省略形です。