Apa arti peta (&: nama) di Ruby?


496

Saya menemukan kode ini di RailsCast :

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

Apa yang (&:name)di map(&:name)berarti?


122
Saya pernah mendengar ini disebut "pretzel colon".
Josh Lee

6
Ha ha. Saya tahu itu sebagai Ampersand. Saya belum pernah mendengarnya disebut "pretzel" tetapi itu masuk akal.
DragonFax

74
Menyebutnya "pretzel colon" menyesatkan, meskipun menarik. Tidak ada "&:" di ruby. Ampersand (&) adalah "operator ampersand unary" dengan simbol push together :. Jika ada, itu adalah "simbol pretzel". Hanya mengatakan.
fontno

3
tags.map (&: name) adalah pengurutan dari tags.map {| s | s.name}
kaushal sharma

3
"pretzel colon" terdengar seperti kondisi medis yang menyakitkan ... tapi saya suka nama untuk simbol ini :)
zmorris

Jawaban:


517

Ini singkatan untuk tags.map(&:name.to_proc).join(' ')

Jika fooobjek dengan to_procmetode, maka Anda bisa meneruskannya ke metode sebagai &foo, yang akan memanggil foo.to_procdan menggunakannya sebagai blok metode.

The Symbol#to_procMetode ini awalnya ditambahkan oleh ActiveSupport tetapi telah diintegrasikan ke dalam Ruby 1.8.7. Ini implementasinya:

class Symbol
  def to_proc
    Proc.new do |obj, *args|
      obj.send self, *args
    end
  end
end

42
Ini jawaban yang lebih baik dari saya.
Oliver N.

91
tags.map (: name.to_proc) sendiri merupakan kependekan dari tags.map {| tag | tag.name}
Simone Carletti

5
ini bukan kode ruby ​​yang valid, Anda masih memerlukan &, yaitutags.map(&:name.to_proc).join(' ')
horseyguy

5
Simbol # to_proc diimplementasikan dalam C, bukan di Ruby, tetapi akan terlihat seperti di Ruby.
Andrew Grimm

5
@AndrewGrimm pertama kali ditambahkan di Ruby on Rails, menggunakan kode itu. Itu kemudian ditambahkan sebagai fitur ruby ​​asli di versi 1.8.7.
Cameron Martin

175

Steno keren lainnya, tidak diketahui banyak orang, adalah

array.each(&method(:foo))

yang merupakan singkatan untuk

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

Dengan memanggil method(:foo)kita mengambil Methodobjek dari selfyang mewakili foometodenya, dan menggunakan &untuk menandakan bahwa ia memiliki to_proc metode yang mengubahnya menjadi a Proc.

Ini sangat berguna ketika Anda ingin melakukan hal - hal gaya point-free . Contohnya adalah untuk memeriksa apakah ada string dalam array yang sama dengan string "foo". Ada cara konvensional:

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

Dan ada cara bebas-point:

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

Cara yang disukai harus yang paling mudah dibaca.


25
array.each{|e| foo(e)}masih lebih pendek :-) +1 lagian
Jared Beck

Bisakah Anda memetakan konstruktor dari kelas lain menggunakan &method?
prinsip holografik

3
@ finishingmove ya saya kira. Coba ini[1,2,3].map(&Array.method(:new))
Gerry

78

Ini setara dengan

def tag_names
  @tag_names || tags.map { |tag| tag.name }.join(' ')
end

45

Sementara mari kita perhatikan juga bahwa ampersand #to_procmagic dapat bekerja dengan semua kelas, bukan hanya Symbol. Banyak Rubyist memilih untuk mendefinisikan #to_procpada kelas Array:

class Array
  def to_proc
    proc { |receiver| receiver.send *self }
  end
end

# And then...

[ 'Hello', 'Goodbye' ].map &[ :+, ' world!' ]
#=> ["Hello world!", "Goodbye world!"]

Ampersand &bekerja dengan mengirim to_procpesan pada operannya, yang, dalam kode di atas, adalah dari kelas Array. Dan karena saya mendefinisikan #to_procmetode pada Array, barisnya menjadi:

[ 'Hello', 'Goodbye' ].map { |receiver| receiver.send( :+, ' world!' ) }

Ini emas murni!
kubak

38

Ini singkatan untuk tags.map { |tag| tag.name }.join(' ')


Tidak, ini ada di Ruby 1.8.7 ke atas.
Chuck

Apakah ini idiom sederhana untuk peta atau Ruby selalu mengartikan '&' dengan cara tertentu?
collimarco

7
@collimarco: Seperti yang dikatakan jleedev dalam jawabannya, &operator unary memanggil to_procoperannya. Jadi itu tidak spesifik untuk metode peta, dan pada kenyataannya bekerja pada metode apa pun yang mengambil blok dan melewati satu atau lebih argumen ke blok.
Chuck

36
tags.map(&:name)

sama dengan

tags.map{|tag| tag.name}

&:name hanya menggunakan simbol sebagai nama metode untuk dipanggil.


1
Jawaban yang saya cari, bukan khusus untuk procs (tapi itu adalah pertanyaan pemohon)
matrim_c

Jawaban bagus! diperjelas untuk saya dengan baik.
apadana

14

Jawaban Josh Lee hampir benar kecuali bahwa kode Ruby yang setara seharusnya adalah sebagai berikut.

class Symbol
  def to_proc
    Proc.new do |receiver|
      receiver.send self
    end
  end
end

tidak

class Symbol
  def to_proc
    Proc.new do |obj, *args|
      obj.send self, *args
    end
  end
end

Dengan kode ini, ketika print [[1,'a'],[2,'b'],[3,'c']].map(&:first)dijalankan, Ruby membagi input pertama [1,'a']menjadi 1 dan 'a' untuk memberikan obj1 dan args*'a' untuk menyebabkan kesalahan karena objek Fixnum 1 tidak memiliki metode sendiri (yaitu: pertama).


Kapan [[1,'a'],[2,'b'],[3,'c']].map(&:first)dieksekusi;

  1. :firstadalah objek Simbol, jadi ketika &:firstdiberikan kepada metode peta sebagai parameter, Simbol # to_proc dipanggil.

  2. map mengirim pesan panggilan ke: first.to_proc dengan parameter [1,'a'], mis :first.to_proc.call([1,'a']). dijalankan.

  3. prosedur to_proc di kelas Simbol mengirimkan pesan kirim ke objek array ( [1,'a']) dengan parameter (: pertama), misalnya, [1,'a'].send(:first)dijalankan.

  4. iterates atas sisa elemen dalam [[1,'a'],[2,'b'],[3,'c']]objek.

Ini sama dengan mengeksekusi [[1,'a'],[2,'b'],[3,'c']].map(|e| e.first)ekspresi.


1
Jawaban Josh Lee benar - benar benar, seperti yang dapat Anda lihat dengan memikirkan [1,2,3,4,5,6].inject(&:+)- menyuntikkan mengharapkan lambda dengan dua parameter (memo dan item) dan :+.to_procmengirimkannya - Proc.new |obj, *args| { obj.send(self, *args) }atau{ |m, o| m.+(o) }
Uri Agassi

11

Ada dua hal yang terjadi di sini, dan penting untuk memahami keduanya.

Seperti dijelaskan dalam jawaban lain, Symbol#to_procmetode ini dipanggil.

Tapi alasannya to_procdipanggil pada simbol adalah karena dilewatkan mapsebagai argumen blok. Menempatkan &di depan argumen dalam pemanggilan metode menyebabkannya diteruskan dengan cara ini. Ini berlaku untuk metode Ruby apa pun, tidak hanya mapdengan simbol.

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)

Akan Symboldikonversi ke Prockarena dilewatkan sebagai blok. Kami dapat menunjukkan ini dengan mencoba meneruskan proc ke .maptanpa ampersand:

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

Meskipun tidak perlu dikonversi, metode ini tidak akan tahu bagaimana menggunakannya karena mengharapkan argumen blok. Melewati dengan &memberi .mapblok yang diharapkannya.


Ini jujur ​​jawaban terbaik yang diberikan. Anda menjelaskan mekanisme di belakang ampersand dan mengapa kita berakhir dengan proc, yang saya tidak dapatkan sampai jawaban Anda. Terima kasih.
Fralcon

5

(&: name) adalah kependekan dari (&: name.to_proc) sama dengan tags.map{ |t| t.name }.join(' ')

to_proc sebenarnya diimplementasikan dalam C


5

peta (&: nama) mengambil objek enumerable (memberi tag pada kasus Anda) dan menjalankan metode nama untuk setiap elemen / tag, menghasilkan setiap nilai yang dikembalikan dari metode.

Ini adalah singkatan untuk

array.map { |element| element.name }

yang mengembalikan array nama elemen (tag)


3

Ini pada dasarnya menjalankan pemanggilan metode tag.namepada setiap tag dalam array.

Ini adalah steno ruby ​​yang disederhanakan.


2

Meskipun kami sudah memiliki jawaban yang luar biasa, melihat melalui perspektif seorang pemula saya ingin menambahkan informasi tambahan:

Apa arti peta (&: nama) di Ruby?

Ini berarti, bahwa Anda meneruskan metode lain sebagai parameter ke fungsi peta. (Pada kenyataannya Anda melewati simbol yang akan dikonversi menjadi proc. Tapi ini tidak begitu penting dalam kasus khusus ini).

Yang penting adalah Anda memiliki methodnama nameyang akan digunakan oleh metode peta sebagai argumen alih-alih blockgaya tradisional .


2

Pertama, &:nameadalah jalan pintas untuk &:name.to_proc, di mana :name.to_procmengembalikan Proc(sesuatu yang mirip, tetapi tidak identik dengan lambda) yang ketika dipanggil dengan objek sebagai argumen (pertama), memanggil namemetode pada objek itu.

Kedua, sementara &di def foo(&block) ... endmualaf blok diteruskan ke fooke Proc, itu tidak sebaliknya bila diterapkan pada Proc.

Dengan demikian, &:name.to_procadalah blok yang mengambil objek sebagai argumen dan memanggil namemetode di atasnya, yaitu { |o| o.name }.


1

Berikut :nameadalah simbol yang menunjuk ke metode nameobjek tag. Ketika kita beralih &:nameke map, itu akan memperlakukan namesebagai objek proc. Singkatnya, tags.map(&:name)bertindak sebagai:

tags.map do |tag|
  tag.name
end


0

Sama seperti di bawah ini:

def tag_names
  if @tag_names
    @tag_names
  else
    tags.map{ |t| t.name }.join(' ')
end
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.