Rubyのmap(&:method)構文に引数を指定できますか?


116

おそらく、次のRubyの略記(a配列です)に精通していると思います。

a.map(&:method)

たとえば、irbで次のことを試してください。

>> a=[:a, 'a', 1, 1.0]
=> [:a, "a", 1, 1.0]
>> a.map(&:class)
=> [Symbol, String, Fixnum, Float]

構文a.map(&:class)は、a.map {|x| x.class}

この構文の詳細については、「Rubyでのmap(&:name)の意味?」を参照してください。

構文を通じて、各配列要素に対して&:classメソッド呼び出しclassを行っています。

私の質問は、メソッド呼び出しに引数を提供できるかどうかです。もしそうなら、どのように?

たとえば、次の構文をどのように変換しますか

a = [1,3,5,7,9]
a.map {|x| x + 2}

&:構文?

&:構文が優れていることを示唆しているわけではありません。&:引数付きの構文を使用するメカニズムにのみ興味があります。

これは+Integerクラスのメソッドだと知っていると思います。あなたはirbで以下を試すことができます:

>> a=1
=> 1
>> a+(1)
=> 2
>> a.send(:+, 1)
=> 2

回答:


139

次のSymbolような簡単なパッチを作成できます。

class Symbol
  def with(*args, &block)
    ->(caller, *rest) { caller.send(self, *rest, *args, &block) }
  end
end

これだけでなく、これを行うことができます:

a = [1,3,5,7,9]
a.map(&:+.with(2))
# => [3, 5, 7, 9, 11] 

しかし、複数のパラメータを渡すなど、他の多くのクールなものもあります:

arr = ["abc", "babc", "great", "fruit"]
arr.map(&:center.with(20, '*'))
# => ["********abc*********", "********babc********", "*******great********", "*******fruit********"]
arr.map(&:[].with(1, 3))
# => ["bc", "abc", "rea", "rui"]
arr.map(&:[].with(/a(.*)/))
# => ["abc", "abc", "at", nil] 
arr.map(&:[].with(/a(.*)/, 1))
# => ["bc", "bc", "t", nil] 

さらにinject、ブロックに2つの引数を渡すでも機能します。

%w(abecd ab cd).inject(&:gsub.with('cde'))
# => "cdeeecde" 

または、[速記]ブロック速記ブロックに渡すことで非常にクールなもの:

[['0', '1'], ['2', '3']].map(&:map.with(&:to_i))
# => [[0, 1], [2, 3]]
[%w(a b), %w(c d)].map(&:inject.with(&:+))
# => ["ab", "cd"] 
[(1..5), (6..10)].map(&:map.with(&:*.with(2)))
# => [[2, 4, 6, 8, 10], [12, 14, 16, 18, 20]] 

@ArupRakshitとの会話でさらに詳しく説明します。Ruby
のmap(&:method)構文に引数を指定できますか?


以下コメントで @amcaplanが提案したように、withメソッドの名前をに変更すると、短い構文を作成できますcall。この場合、rubyにはこの特別なメソッドの組み込みのショートカットがあり.()ます。

したがって、上記を次のように使用できます。

class Symbol
  def call(*args, &block)
    ->(caller, *rest) { caller.send(self, *rest, *args, &block) }
  end
end

a = [1,3,5,7,9]
a.map(&:+.(2))
# => [3, 5, 7, 9, 11] 

[(1..5), (6..10)].map(&:map.(&:*.(2)))
# => [[2, 4, 6, 8, 10], [12, 14, 16, 18, 20]] 

5
これがRubyコアの一部であるといいのですが。
Jikku Jose、2015

6
@UriAgassi多くのライブラリがこれを行うからといって、それが良い習慣になるわけではありません。しながら、Symbol#withコアライブラリに存在し、そのメソッドを定義することは、依然として変化している既存の方法(即ち、上書き)ルビーライブラリのコアクラスの実装を再定義未満で破壊的でないかもしれません。練習は非常に慎重かつ慎重に行う必要があります。\ n \ n既存のクラスから継承し、新しく作成したクラスを変更することを検討してください。これは一般的に、ルビのコアクラスを変更することによる悪影響を伴わずに同等の結果を達成します。
rudolph9

2
@ rudolph9 - I BEG異なること- 「上書き」の定義は書くことでオーバー書かれたコードが利用できない長いされていることを意味何かを、これは明らかにそうではありません。Symbolクラスを継承するというあなたの提案について-それはそのようなコアクラス(newたとえば、メソッドがない)であり、その使用法は(可能な場合でも)面倒であり、拡張の目的...それを使用する実装を示し、同等の結果を達成できる場合-共有してください!
Uri Agassi

3
私はこの解決策が好きですが、あなたはそれでもっともっと楽しくなると思います。withメソッドを定義する代わりに、を定義しますcall。次に、メソッドを使用しているa.map(&:+.(2))ためobject.()、次のようなことができます#call。そして、あなたがそれ:+.(2).(3) #=> 5に取り組んでいる間、あなたは次のような楽しいものを書くことができます-一種のLISPyを感じますね?
amcaplan

2
これをコアで見たい-砂糖ala .map(&:foo)を使用できる一般的なパターン
Stephen

48

あなたの例のために行うことができますa.map(&2.method(:+))

Arup-iMac:$ pry
[1] pry(main)> a = [1,3,5,7,9]
=> [1, 3, 5, 7, 9]
[2] pry(main)> a.map(&2.method(:+))
=> [3, 5, 7, 9, 11]
[3] pry(main)> 

これがどのように機能するかです:-

[3] pry(main)> 2.method(:+)
=> #<Method: Fixnum#+>
[4] pry(main)> 2.method(:+).to_proc
=> #<Proc:0x000001030cb990 (lambda)>
[5] pry(main)> 2.method(:+).to_proc.call(1)
=> 3

2.method(:+)Methodオブジェクトを与えます。次に &2.method(:+)実際#to_procには、それをProcオブジェクトにする呼び出しメソッドです。次に、Rubyで&:演算子を何と呼びますか?


賢い使い方!メソッドの呼び出しを両方の方法で適用できると想定していますか(arr [element] .method(param)=== param.method(arr [element]))または混乱していますか?
Kostas Rousis

@rkon私もあなたの質問を聞きませんでした。しかし、Pry上記の出力が表示されれば、それを取得できます。
Arup Rakshit、2014年

5
@rkonどちらの方法でも機能しません。+可換であるため、この特定のケースで機能します。
澤沢2014年

複数の引数をどのように提供できますか?この場合のように:a.map {| x | x.method(1,2,3)}
Zack Xu

1
私のポイント@sawa :)あなたはXで各番号を分割したい場合は、別の方法またはLETだと言うためではありません+で理にかなっていますが、ということだと
コスタスRousis

11

あなたが確認にリンクポストとして、a.map(&:class)の省略形ではありませんa.map {|x| x.class}が、ためa.map(&:class.to_proc)

これはto_proc&演算子に続くもので呼び出されることを意味します。

したがって、Proc代わりに直接それを与えることができます:

a.map(&(Proc.new {|x| x+2}))

私はおそらくこれがあなたの質問の目的に反することを知っていますが、それを回避する方法は他にありません-呼び出されるメソッドを指定するのではなく、応答するものを渡すだけto_procです。


1
また、プロシージャをローカル変数に設定して、マップに渡すことができることにも注意してください。 my_proc = Proc.new{|i| i + 1}[1,2,3,4].map(&my_proc) => [2,3,4,5]
rudolph9

10

短い答え:いいえ。

@rkonの回答に続いて、これを行うこともできます。

a = [1,3,5,7,9]
a.map &->(_) { _ + 2 } # => [3, 5, 7, 9, 11]

9
あなたは正しいですが、私&->(_){_ + 2}はそれより短いとは思いません{|x| x + 2}

1
それはそうではありません、それは@rkonが彼の答えで言っていることなので、私はそれを繰り返さなかった。
Agis 2014年

2
@Agisあなたの答えは短くはありませんが、見栄えがします。
Jikku Jose

1
それは素晴らしいソリューションです。
BenMorganIO 2015

5

受け入れられた回答のように、自分でコアクラスにパッチを適用する代わりに、ファセットgemの機能を使用する方が短くてクリーンです。

require 'facets'
a = [1,3,5,7,9]
a.map &:+.(2)

5

私の意見では、列挙型の別のネイティブオプションが2つだけあります。クラスにEnumerableは、with_object別のメソッドを返すメソッドがありますEnumerable

したがって&、各アイテムとオブジェクトを引数として、メソッドの演算子を呼び出すことができます。

例:

a = [1,3,5,7,9]
a.to_enum.with_object(2).map(&:+) # => [3, 5, 7, 9, 11]

より多くの引数が必要な場合は、プロセスを繰り返す必要がありますが、私の見方では醜いです。

a = [1,3,5,7,9]
a.to_enum.with_object(2).map(&:+).to_enum.with_object(5).map(&:+) # => [8, 10, 12, 14, 16]

0

Symbol#withすでに投稿されているかどうかはわかりませんが、かなり簡略化してあり、うまく機能します。

class Symbol
  def with(*args, &block)
    lambda { |object| object.public_send(self, *args, &block) }
  end
end

(プライベートメソッドの呼び出しを防ぐためにのpublic_send代わりに使用しsendます。これもcallerrubyによってすでに使用されているため、これは混乱を招きました)

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