1. ホーム
  2. ruby

[解決済み] Rubyのmap(&:method)構文に引数を与えることは可能か?

2022-07-06 09:21:28

質問

あなたはおそらく次のような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

どのように解決するのですか?

簡単なパッチを 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" 

あるいは、[shorthand] ブロックを渡すような超クールなもの を省略記法ブロックに渡すような超クールな方法もあります。

[['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]]