Frage Können Sie Argumente für die Map-Syntax (&: method) in Ruby angeben?


Sie kennen wahrscheinlich die folgende Ruby-Kurzschrift (a ist ein Array):

a.map(&:method)

Versuchen Sie beispielsweise Folgendes in Irb:

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

Die Syntax a.map(&:class) ist eine Abkürzung für a.map {|x| x.class}.

Lesen Sie mehr über diese Syntax in "Was bedeutet map (&: name) in Ruby?".

Durch die Syntax &:class, du machst einen Methodenaufruf class für jedes Array-Element.

Meine Frage ist: Können Sie dem Methodenaufruf Argumente liefern? Und wenn ja, wie?

Zum Beispiel, wie konvertieren Sie die folgende Syntax

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

zum &: Syntax?

Ich schlage das nicht vor &: Syntax ist besser. Ich interessiere mich nur für die Mechanismen der Verwendung der &: Syntax mit Argumenten.

Ich nehme an, Sie wissen das + ist eine Methode für die Integer-Klasse. Sie können Folgendes in irb versuchen:

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

76
2018-05-16 13:01


Ursprung


Antworten:


Sie können einen einfachen Patch erstellen Symbol so was:

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

Was Ihnen ermöglichen wird, nicht nur das zu tun:

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

Aber auch viele andere coole Sachen, wie das Übergeben mehrerer Parameter:

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] 

Und sogar mit arbeiten inject, die zwei Argumente an den Block übergibt:

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

Oder etwas super cooles, wenn man [Stenografierblöcke] passiert zu der Kurzschriftblock:

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

Hier ist ein Gespräch, das ich mit @ArupRakshit geführt habe und es weiter erklärt habe:
Können Sie Argumente für die Map-Syntax (&: method) in Ruby angeben?


Wie @amcaplan in der kommentiere unten, könnten Sie eine kürzere Syntax erstellen, wenn Sie die umbenennen with Methode zu call. In diesem Fall verfügt Ruby über eine integrierte Verknüpfung für diese spezielle Methode .().

So könntest du das obige wie folgt verwenden:

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

110
2018-05-17 12:55



Für Ihr Beispiel kann getan werden 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)> 

So funktioniert es: -

[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(:+) gibt ein Method Objekt. Dann &, auf 2.method(:+)eigentlich ein Anruf #to_proc Methode, die es zu einem macht Proc Objekt. Dann folge Wie nennt man den Operator &: in Ruby?.


35
2018-05-16 13:17



Wie der Beitrag, den Sie verlinkt haben, bestätigt, a.map(&:class) ist keine Abkürzung für a.map {|x| x.class} aber für a.map(&:class.to_proc).

Das bedeutet, dass to_proc ist aufgerufen, was auch immer dem folgt & Operator.

So könntest du es dir direkt geben Proc stattdessen:

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

Ich weiß, dass dies den Zweck Ihrer Frage höchstwahrscheinlich vereitelt, aber ich kann keinen anderen Weg um sie herum sehen - es ist nicht so, dass Sie angeben, welche Methode aufgerufen werden soll, Sie geben einfach etwas weiter, das darauf reagiert to_proc.


9
2018-05-16 13:13



Kurze Antwort: Nein.

Nach @ rkon's Antwort könntest du das auch tun:

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

7
2018-05-16 13:41



Anstatt Core-Klassen selbst zu patchen, wie in der angenommenen Antwort, ist es kürzer und sauberer, die Funktionalität des zu verwenden Facetten gem:

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

4
2017-08-26 14:24