Pertanyaan Bisakah Anda memberikan argumen ke sintaks peta (&: metode) di Ruby?


Anda mungkin akrab dengan tulisan singkat Ruby berikut ini (a adalah sebuah array):

a.map(&:method)

Misalnya, coba yang berikut dalam irb:

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

Sintaksnya a.map(&:class) adalah singkatan untuk a.map {|x| x.class}.

Baca lebih lanjut tentang sintaks ini di "Apa arti peta (&: nama) di Ruby?".

Melalui sintaks &:class, Anda membuat panggilan metode class untuk setiap elemen array.

Pertanyaan saya adalah: bisakah Anda memberikan argumen ke pemanggilan metode? Dan jika ya, bagaimana caranya?

Misalnya, bagaimana Anda mengubah sintaks berikut

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

ke &: sintaksis?

Saya tidak menyarankan itu &: sintaks lebih baik. Saya hanya tertarik pada mekanisme penggunaan &: sintaks dengan argumen.

Saya berasumsi Anda tahu itu + adalah metode pada kelas Integer. Anda dapat mencoba yang berikut ini dalam irb:

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

76
2018-05-16 13:01


asal


Jawaban:


Anda dapat membuat tambalan sederhana Symbol seperti ini:

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

Yang akan memungkinkan Anda melakukan tidak hanya ini:

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

Tetapi juga banyak hal keren lainnya, seperti melewatkan beberapa 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] 

Dan bahkan bekerja dengan inject, yang melewati dua argumen ke blok:

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

Atau sesuatu yang sangat keren karena melewati blok [singkatan] untuk blok singkatan:

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

Berikut adalah percakapan saya dengan @ArupRakshit yang menjelaskan lebih lanjut:
Bisakah Anda memberikan argumen ke sintaks peta (&: metode) di Ruby?


Seperti yang disarankan @amcaplan di komentar dibawah, Anda bisa membuat sintaks yang lebih pendek, jika Anda mengganti nama with metode untuk call. Dalam hal ini, ruby ​​memiliki jalan pintas yang dibangun untuk metode khusus ini .().

Jadi Anda bisa menggunakan yang di atas seperti ini:

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



Untuk contoh Anda bisa dilakukan 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)> 

Berikut ini cara kerjanya: -

[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(:+) memberi a Method obyek. Kemudian &, di 2.method(:+), sebenarnya panggilan telepon #to_proc metode, yang membuatnya menjadi Proc obyek. Kemudian ikuti Apa yang Anda sebut operator &: di Ruby?.


35
2018-05-16 13:17



Karena pos yang Anda tautkan untuk mengonfirmasi, a.map(&:class) bukan singkatan untuk a.map {|x| x.class} tapi untuk a.map(&:class.to_proc).

Ini artinya itu to_proc dipanggil apa pun yang mengikuti & operator.

Jadi Anda bisa langsung memberikannya Proc sebagai gantinya:

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

Saya tahu bahwa kemungkinan besar hal ini mengalahkan tujuan pertanyaan Anda, tetapi saya tidak dapat melihat dengan cara lain - bukan bahwa Anda menentukan metode mana yang harus dipanggil, Anda hanya menyampaikan sesuatu yang menanggapi to_proc.


9
2018-05-16 13:13



Jawaban singkat: Tidak.

Mengikuti jawaban @ rkky, Anda juga bisa melakukan ini:

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

7
2018-05-16 13:41



Daripada menambal kelas inti sendiri, seperti pada jawaban yang diterima, itu lebih pendek dan lebih bersih untuk menggunakan fungsi dari Permata facets:

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

4
2017-08-26 14:24