Harita (&: ad) Ruby'de ne anlama geliyor?


496

Bu kodu bir RailsCast içinde buldum :

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

Neyi (&:name)de map(&:name)ortalama?


122
Bu arada “pretzel kolon” ​​diye duydum.
Josh Lee

6
Haha. Bunu "Ve" işareti olarak biliyorum. "Tuzlu kraker" olarak adlandırıldığını hiç duymadım ama bu mantıklı.
DragonFax

74
Ona rağmen "tuzlu kraker" olarak adlandırmak yanıltıcıdır. Yakutta "&:" yoktur. Ve işareti (&), birlikte itilmiş bir "tekli ve işareti operatörü" dür: sembolü. Eğer bir şey varsa, bu bir "tuzlu kraker sembolü". Sadece söylüyorum.
fontno

3
tags.map (&: ad), tags.map {| s | s.name}
sharma

3
"tuzlu kraker" acı verici bir tıbbi durum gibi geliyor ... ama bu sembolün adını seviyorum :)
zmorris

Yanıtlar:


517

İçin kestirme tags.map(&:name.to_proc).join(' ')

Eğer foobir ile bir nesnedir to_procyöntemle, o zaman gibi bir yönteme geçirebilirsiniz &fooarayacak foo.to_procve yöntemin bloğu olarak kullanılması söyledi.

Symbol#to_procYöntem ilk ActiveSupport ilave edildi ancak Ruby 1.8.7 entegre edilmiştir. Bu onun uygulamasıdır:

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

41
Bu benimkinden daha iyi bir cevap.
Oliver N.

91
tags.map (: name.to_proc), tags.map {| tag | tag.name}
Simone Carletti

5
bu geçerli yakut kodu değil, hala ihtiyacınız var &, yanitags.map(&:name.to_proc).join(' ')
horseyguy

5
# To_proc sembolü Ruby'de değil C'de uygulanır, ancak Ruby'de böyle görünecektir.
Andrew Grimm

5
@ AndrewGrimm ilk önce Ruby on Rails'e bu kodu kullanarak eklendi. Daha sonra 1.8.7 versiyonunda yerel bir yakut özelliği olarak eklendi.
Cameron Martin

175

Birçok kişi tarafından bilinmeyen bir başka havalı steno

array.each(&method(:foo))

ki bu bir kısayol

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

Çağırarak , yöntemini temsil eden method(:foo)bir Methodnesneyi aldık ve onu bir nesnesine dönüştüren bir yöntemi olduğunu belirtmek için kullandık .selffoo&to_proc Proc

Noktasız stil işleri yapmak istediğinizde bu çok kullanışlıdır . Bir örnek, bir dizede dizeye eşit herhangi bir dize olup olmadığını kontrol etmektir "foo". Geleneksel bir yol var:

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

Ve anlamsız bir yol var:

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

Tercih edilen yol en okunabilir yöntem olmalıdır.


25
array.each{|e| foo(e)}yine de daha kısa :-) +1 yine de
Jared Beck

Kullanarak başka bir sınıfın kurucusunu eşleyebilir misiniz &method?
holografik prensip

3
@finishingmove evet sanırım. Deneyin[1,2,3].map(&Array.method(:new))
Gerry

78

Eşittir

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

45

Ve işareti ve #to_procbüyünün sadece Symbol ile değil, herhangi bir sınıfla çalışabileceğini de not edelim . Birçok Rubyist #to_proc, Array sınıfında tanımlamayı seçer :

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

# And then...

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

Ampersand , yukarıdaki kodda Array sınıfından olan işleneni üzerinde mesaj &göndererek çalışır to_proc. Ve #to_procArray üzerinde yöntem tanımladığımdan beri , çizgi şöyle olur:

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

Bu saf altın!
kubak

38

İçin kestirme tags.map { |tag| tag.name }.join(' ')


Hayır, Ruby 1.8.7 ve üstünde.
Chuck

Harita için basit bir deyim mi yoksa Ruby her zaman '&' kelimesini belirli bir şekilde mi yorumluyor?
collimarco

7
@collimarco: Jleedev'in cevabında söylediği gibi, tekli &operatör to_procoperandını çağırıyor . Bu nedenle, harita yöntemine özgü değildir ve aslında bir blok alan ve bloğa bir veya daha fazla argüman ileten herhangi bir yöntem üzerinde çalışır.
Chuck

36
tags.map(&:name)

aynıdır

tags.map{|tag| tag.name}

&:name sembolü çağrılacak yöntem adı olarak kullanır.


1
Özellikle procs için değil, aradığım cevap (ancak bu talep
edenlerin sorusuydu

Güzel cevap! benim için iyi açıkladı.
apadana

14

Josh Lee'nin cevabı, eşdeğer Ruby kodunun aşağıdaki gibi olması dışında neredeyse doğrudur.

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

değil

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

Bu kodla print [[1,'a'],[2,'b'],[3,'c']].map(&:first)yürütüldüğünde, Ruby ilk girişi [1,'a']1'e ve 'a' ya böler ve obj1 ve args*'a' hatalarına neden olur, çünkü Fixnum nesnesi 1 kendi kendine (ilk:) yöntemine sahip değildir.


Ne zaman [[1,'a'],[2,'b'],[3,'c']].map(&:first)yürütülür;

  1. :firstbir Symbol nesnesidir, bu nedenle &:firstbir harita yöntemine parametre olarak verildiğinde, Symbol # to_proc çağrılır.

  2. map [1,'a'], :first.to_proc.call([1,'a'])şu parametreye sahip first.to_proc öğesine çağrı mesajı gönderir , örn . yürütülür.

  3. Symbol sınıfındaki to_proc yordamı, [1,'a'](: first) parametresiyle bir dizi nesnesine ( ) bir gönderme iletisi gönderir , örn [1,'a'].send(:first). yürütülür.

  4. [[1,'a'],[2,'b'],[3,'c']]nesnedeki diğer öğelerin üzerinde yinelenir .

Bu, [[1,'a'],[2,'b'],[3,'c']].map(|e| e.first)ifadenin yürütülmesi ile aynıdır .


1
Josh Lee'nin cevabı kesinlikle doğru, düşünerek görebileceğiniz gibi [1,2,3,4,5,6].inject(&:+)- enjekte etmek iki parametreli (not ve öğe) bir lambda bekler ve :+.to_procsunar - Proc.new |obj, *args| { obj.send(self, *args) }veya{ |m, o| m.+(o) }
Uri Agassi

11

Burada iki şey oluyor ve ikisini de anlamak önemlidir.

Diğer cevaplarda açıklandığı gibi, Symbol#to_proc yöntem çağrılmaktadır.

Ancak to_procsembolün üzerinde durulması nedeni , mapblok argüman olarak aktarılmasıdır . &Bir yöntem çağrısında bir argümanın önüne yerleştirilmesi , argümanın bu şekilde iletilmesine neden olur. Bu sadece mapsembollerle değil, herhangi bir Ruby yöntemi için de geçerlidir .

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)

Bir , bir blok olarak geçirildiği Symboliçin dönüştürülür Proc. .mapVe işareti olmadan bir proc geçirmeye çalışarak bunu gösterebiliriz :

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

Dönüştürülmesi gerekmese de, yöntem bir blok argümanı beklediğinden nasıl kullanılacağını bilemez. İle geçmek beklediği bloğu &verir .map.


Bu dürüstçe verilen en iyi cevaptır. Ve işareti arkasındaki mekanizmayı ve neden cevabınıza kadar alamadığım bir proc ile sonuçlandığımızı açıklıyorsunuz. Teşekkür ederim.
Fralcon

5

(&: name), (&: name.to_proc) için kısadır ve tags.map{ |t| t.name }.join(' ')

to_proc aslında C dilinde uygulanır


5

map (&: name) numaralandırılabilir bir nesne (sizin durumunuzdaki etiketler ) alır ve her bir öğe / etiket için ad yöntemini çalıştırır ve döndürülen her değeri yöntemden çıkarır.

İçin bir kısayol

array.map { |element| element.name }

öğe (etiket) adları dizisini döndüren


3

Temelde tag.namedizideki her etiket üzerinde yöntem çağrısını yürütür .

Basitleştirilmiş bir yakut stenondur.


2

Şimdiden harika cevaplarımız olmasına rağmen, yeni başlayanların bakış açısıyla baktığımda ek bilgiler eklemek istiyorum:

Harita (&: ad) Ruby'de ne anlama geliyor?

Bu, harita işlevine parametre olarak başka bir yöntem geçirdiğiniz anlamına gelir. (Gerçekte bir proc'a dönüştürülen bir sembol geçiriyorsunuz. Ancak bu, bu özel durumda önemli değil).

Önemli olan size bir olmasıdır methodadında nameyerine geleneksel bir argüman olarak harita yöntemiyle kullanılan olacağını blocktarzı.


2

İlk olarak, &:namebir kısayol &:name.to_proc, burada :name.to_procdöner bir Proc(birinci) bağımsız değişken, aramaları gibi bir nesne ile arandığı zaman (benzer bir şey, ancak bir lambda özdeş olan) namebu nesne ile ilgili bir yöntem.

İkinci olarak, &içinde def foo(&block) ... enddönüştürür bir blok geçirilen foobir için Proc, bir tatbik tersini yapar Proc.

Böylece, &:name.to_procbir nesneyi argüman olarak alan ve nameüzerindeki yöntemi çağıran bir bloktur { |o| o.name }.


1

İşte tag nesnesi :nameyöntemine işaret eden sembol name. Biz geçtiğinizde &:nameiçin map, tedavi edecek namebir proc nesnesi olarak. Kısacası şu şekilde tags.map(&:name)hareket eder:

tags.map do |tag|
  tag.name
end


0

Aşağıdaki ile aynı:

def tag_names
  if @tag_names
    @tag_names
  else
    tags.map{ |t| t.name }.join(' ')
end
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.