Rubyでmap(&:name)はどういう意味ですか?


496

RailsCastでこのコードを見つけました:

def tag_names
  @tag_names || tags.map(&:name).join(' ')
end

何を(&:name)してmap(&:name)意味ですか?


122
ちなみにこれは「プレッツェルコロン」と呼ばれています。
Josh Lee

6
はは。私はアンパサンドとしてそれを知っています。「プレッツェル」と呼ばれることは聞いたことがありませんが、それは理にかなっています。
DragonFax 2013

74
キャッチーですが、「プレッツェルコロン」と呼ぶのは誤解を招きます。ルビには「&:」はありません。アンパサンド(&)は、プッシュされた:symbolを持つ「単項アンパサンド演算子」です。どちらかというと「プレッツェルのシンボル」です。ただ言って。
fontno 2013

3
tags.map(&:name)はtags.map {| s |の一種です s.name}
kaushal sharma 16

3
「プレッツェルコロン」は痛みを伴う病状のように聞こえますが、私はこのシンボルの名前が好きです:)
zmorris

回答:


517

それは略記です 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

42
これは私のものよりも良い答えです。
Oliver N.

91
tags.map(:name.to_proc)自体は、tags.map {| tag |の省略形です。tag.name}
Simone Carletti、2009

5
これは有効なrubyコードではありません&。つまり、tags.map(&:name.to_proc).join(' ')
horseyguy '25 / 06/25

5
Symbol#to_procはRubyではなくCで実装されていますが、Rubyではこのようになっています。
Andrew Grimm

5
@AndrewGrimmは、Ruby on Railsでそのコードを使用して最初に追加されました。その後、バージョン1.8.7でルビーのネイティブ機能として追加されました。
Cameron Martin

175

多くの人には知られていない別のクールな速記法は

array.each(&method(:foo))

これは略記です

array.each { |element| foo(element) }

を呼び出すmethod(:foo)ことで、そのメソッドを表すMethodオブジェクトを取得し、を使用して、に変換するメソッドがあることを示します。selffoo&to_proc Proc

これは、ポイントフリースタイルを実行する場合に非常に便利です。例として、配列に文字列と等しい文字列があるかどうかを確認します"foo"。従来の方法があります:

["bar", "baz", "foo"].any? { |str| str == "foo" }

そして、ポイントフリーの方法があります:

["bar", "baz", "foo"].any?(&"foo".method(:==))

推奨される方法は、最も読みやすい方法です。


25
array.each{|e| foo(e)}まだ短いです:-)とにかく+1
Jared Beck

別のクラスのコンストラクターを使用してマップできます&methodか?
ホログラフィック原理

3
@finishingmoveそうですね。これを試してください[1,2,3].map(&Array.method(:new))
ジェリー


45

アンパサンド#to_procマジックは、シンボルだけでなく、どのクラスでも機能することにも注意してください。多くの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!' ) }

これは純金です!
kubak

38

それは略記です tags.map { |tag| tag.name }.join(' ')


いいえ、Ruby 1.8.7以降です。
チャック、

それはマップの単純なイディオムですか、それともRubyは常に「&」を特定の方法で解釈しますか?
コリマルコ2009

7
@collimarco:jleedevが彼の答えで言うように、単項&演算子はto_procそのオペランドを呼び出します。したがって、それはmapメソッドに固有のものではなく、実際には、ブロックを受け取り、1つ以上の引数をブロックに渡す任意のメソッドで機能します。
チャック

36
tags.map(&:name)

と同じです

tags.map{|tag| tag.name}

&:name 呼び出されるメソッド名としてシンボルを使用するだけです。


1
特に
procの

いい答えだ!よくわかりました。
アパダナ

14

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」に分割され、obj1とargs*「A」である(第一)メソッド自身を持たないFixnumか対象物1としてエラーを引き起こします。


いつ[[1,'a'],[2,'b'],[3,'c']].map(&:first)実行されるか

  1. :firstSymbolオブジェクトであるため&:first、mapメソッドにパラメーターとして指定すると、Symbol#to_procが呼び出されます。

  2. mapは:first.to_procにパラメータを指定して呼び出しメッセージを送信します[1,'a']。たとえば、:first.to_proc.call([1,'a'])実行されます。

  3. Symbolクラスのto_procプロシージャは、実行メッセージなどの[1,'a']パラメータ(:first)を使用して、配列オブジェクト()に送信メッセージを送信します[1,'a'].send(:first)

  4. [[1,'a'],[2,'b'],[3,'c']]オブジェクトの残りの要素を反復処理します。

これは[[1,'a'],[2,'b'],[3,'c']].map(|e| e.first)式の実行と同じです。


1
ジョシュ・リーの答えは絶対にあなたがについて考えることで見ることができるように、正しい[1,2,3,4,5,6].inject(&:+)-注入の2つのパラメータ(メモやアイテム)とラムダを期待し、:+.to_procそれを実現- Proc.new |obj, *args| { obj.send(self, *args) }または{ |m, o| m.+(o) }
ウリアガシ

11

ここでは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、それがブロックとして渡されているため。.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、期待するブロックが得られます。


これは正直に言って最良の答えです。アンパサンドの背後にあるメカニズムと、なぜprocになるのかを説明してください。ありがとうございました。
Fralcon 2017年

5

(&:name)は(&:name.to_proc)の略で、 tags.map{ |t| t.name }.join(' ')

to_procは実際にはCで実装されています


5

map(&:name)は列挙可能なオブジェクト(この場合はタグ)を取り、要素/タグごとにnameメソッドを実行し、メソッドから返された各値を出力します。

それはの略記です

array.map { |element| element.name }

element(tag)名の配列を返す


3

基本的にtag.nameは、配列内の各タグに対してメソッド呼び出しを実行します。

簡略化されたルビの省略形です。


2

すでにすばらしい回答はありますが、初心者の視点から見て、追加情報を追加したいと思います。

Rubyでmap(&:name)はどういう意味ですか?

これは、map関数にパラメーターとして別のメソッドを渡すことを意味します。(実際には、procに変換されるシンボルを渡します。しかし、これはこの特定のケースではそれほど重要ではありません)。

重要なのは、従来のスタイルの代わりに引数としてマップメソッドで使用されるmethod名前が付いnameていることblockです。


2

1つ目&:nameは、のショートカットです&:name.to_proc。ここで:name.to_procは、Proc(最初の)引数としてオブジェクトを指定して呼び出すと、nameそのオブジェクトのメソッドを呼び出す(ラムダと似ているが、ラムダと同一ではないもの)を返します。

一方、第2は、&def foo(&block) ... end変換ブロックに渡さfooProcAに適用した場合、それは逆の処理を行いProc

したがって、&:name.to_procはオブジェクトを引数として取り、そのnameメソッドを呼び出すブロックです{ |o| o.name }


1

ここで:name方法を指す記号であるnameタグ・オブジェクトの。に渡す&:namemap、それはnameprocオブジェクトとして扱われます。略して、次のようにtags.map(&:name)機能します。

tags.map do |tag|
  tag.name
end


弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.