RailsCastでこのコードを見つけました:
def tag_names
@tag_names || tags.map(&:name).join(' ')
end
(&:name)
inとはどういうmap(&:name)
意味ですか?
RailsCastでこのコードを見つけました:
def tag_names
@tag_names || tags.map(&:name).join(' ')
end
(&:name)
inとはどういうmap(&: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
多くの人に知られていないもう 1 つのクールな省略形は、次のとおりです。
array.each(&method(:foo))
これはの省略形です
array.each { |element| foo(element) }
を呼び出すことで、そのメソッドを表すからオブジェクトmethod(:foo)
を取得し、 を使用して、それを に変換するメソッドがあることを示しました。Method
self
foo
&
to_proc
Proc
これは、ポイントフリースタイルで物事を行いたい場合に非常に便利です。例として、配列内に string と等しい文字列があるかどうかを確認します"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 だけでなく、どのクラスでも機能することに注意してください。多くの Rubyist#to_proc
は Array クラスで定義することを選択します:
class Array
def to_proc
proc { |receiver| receiver.send *self }
end
end
# And then...
[ 'Hello', 'Goodbye' ].map &[ :+, ' world!' ]
#=> ["Hello world!", "Goodbye world!"]
アンパサンドは、上記のコードでは Array クラスのオペランドでメッセージを&
送信することによって機能します。to_proc
そして、#to_proc
配列にメソッドを定義したので、行は次のようになります。
[ 'Hello', 'Goodbye' ].map { |receiver| receiver.send( :+, ' world!' ) }
の略ですtags.map { |tag| tag.name }.join(' ')
ここでは 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 で実装されています
タグオブジェクト:name
のメソッドを指すシンボルを次に示します。name
に渡す&:name
と、proc オブジェクトとしてmap
扱われます。name
要するに、次のようにtags.map(&:name)
機能します。
tags.map do |tag|
tag.name
end
以下と同じです。
def tag_names
if @tag_names
@tag_names
else
tags.map{ |t| t.name }.join(' ')
end