Rubyでmap(&:name)はどういう意味ですか?
-
07-07-2019 - |
質問
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
他のヒント
多くの人には知られていないもう1つのクールな速記は
array.each(&method(:foo))
の短縮形です
array.each { |element| foo(element) }
method(:foo)
を呼び出すことで、 foo
メソッドを表す self
から Method
オブジェクトを取得しました、および&
を使用して、 to_proc
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だけでなく、どのクラスでも機能することに注意してください。多くのRubyistは、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 &
は、上記のコードではArrayクラスのオペランドで to_proc
メッセージを送信することで機能します。そして、Arrayで #to_proc
メソッドを定義したため、行は次のようになります。
[ '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)
がRubyを実行すると、最初の入力 [1、 'a']
が1と 'a'に分割され、 obj
1と args *
'aが得られます'Fixnumオブジェクト1にはメソッドself(:first)がないため、エラーが発生します。
[[1、 'a']、[2、 'b']、[3、 'c']]。map(&:first)
が実行されたとき;
-
:first
はSymbolオブジェクトなので、&:first
がパラメーターとしてマップメソッドに渡されると、Symbol#to_procが呼び出されます。 -
mapは、パラメーター
[1、 'a']
で呼び出しメッセージを:first.to_procに送信します(例::first.to_proc.call([1、 'a' ])
が実行されます。
Symbolクラスの -
to_procプロシージャは、パラメータ(:first)を持つ配列オブジェクト(
[1、 'a']
)に送信メッセージを送信します(例:[1、 'a']。send(:first)
が実行されます。 -
[[1、 'a']、[2、 'b']、[3、 'c']]
オブジェクトの残りの要素について繰り返します。 p>
これは、 [[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"]
変換する必要はありませんが、ブロック引数を想定しているため、メソッドはその使用方法を知りません。それを&amp;
で渡すと、 .map
が期待するブロックを提供します。
(&amp;:name)は(&amp;:name.to_proc)の略で、 tags.map {と同じです| t | t.name} .join( '')
to_procは実際にはCで実装されています
すでに素晴らしい回答がありますが、初心者の視点から見て、追加情報を追加したいと思います:
map(&amp;:name)はRubyで何を意味しますか?
これは、別のメソッドをパラメーターとしてマップ関数に渡すことを意味します。 (実際には、procに変換されるシンボルを渡します。しかし、これはこの特定のケースではそれほど重要ではありません。)
重要なことは、 name
という名前の method
があり、これは従来の block
スタイル。
map(&amp;:name)は列挙可能なオブジェクト(あなたの場合はタグ)を受け取り、各要素/タグに対してnameメソッドを実行し、メソッドから各戻り値を出力します。
これは略記です
array.map { |element| element.name }
要素(タグ)名の配列を返します
ここで:name
は、タグオブジェクトのメソッド name
を指すシンボルです。
&amp;:name
を map
に渡すと、 name
をprocオブジェクトとして扱います。
要するに、 tags.map(&amp;:name)
は次のように機能します:
tags.map do |tag|
tag.name
end
という意味
array.each(&:to_sym.to_proc)
基本的に、配列内の各タグに対してメソッド呼び出し tag.name
を実行します。
これは、簡略化されたルビーの略記です。
以下と同じです:
def tag_names
if @tag_names
@tag_names
else
tags.map{ |t| t.name }.join(' ')
end