Я знайшов цей код на RailsCast :
def tag_names
@tag_names || tags.map(&:name).join(' ')
end
Що робить (&:name)в map(&:name)середньому?
Я знайшов цей код на RailsCast :
def tag_names
@tag_names || tags.map(&:name).join(' ')
end
Що робить (&:name)в map(&:name)середньому?
Відповіді:
Це стенограма для tags.map(&:name.to_proc).join(' ')
Якщо fooце об'єкт із to_procметодом, то ви можете передати його методу як &foo, який буде викликати foo.to_procі використовувати це як блок методу.
Спочатку Symbol#to_procметод був доданий ActiveSupport, але він був інтегрований у Ruby 1.8.7. Це його реалізація:
class Symbol
def to_proc
Proc.new do |obj, *args|
obj.send self, *args
end
end
end
&, тобтоtags.map(&:name.to_proc).join(' ')
Ще одна класна стенограма, невідома багатьом, є
array.each(&method(:foo))
що є скороченням
array.each { |element| foo(element) }
Зателефонувавши, method(:foo)ми взяли Methodоб'єкт, selfякий представляє його fooметод, і використовували &для позначення того, що у нього є to_proc метод, який перетворює його в a Proc.
Це дуже корисно, коли ви хочете робити стиль без точок . Приклад - перевірити, чи є в масиві який-небудь рядок, який дорівнює рядку "foo". Існує звичайний спосіб:
["bar", "baz", "foo"].any? { |str| str == "foo" }
І є безпроблемний спосіб:
["bar", "baz", "foo"].any?(&"foo".method(:==))
Кращий спосіб повинен бути найбільш читабельним.
array.each{|e| foo(e)}все ж коротше :-) +1 у будь-якому разі
&method?
[1,2,3].map(&Array.method(:new))
Це рівнозначно
def tag_names
@tag_names || tags.map { |tag| tag.name }.join(' ')
end
Хоча ми також зазначимо, що амперсанд і #to_procмагія можуть працювати з будь-яким класом, а не лише з Symbol. Багато Rubyists вирішують визначити #to_procклас Array:
class Array
def to_proc
proc { |receiver| receiver.send *self }
end
end
# And then...
[ 'Hello', 'Goodbye' ].map &[ :+, ' world!' ]
#=> ["Hello world!", "Goodbye world!"]
Ampersand &працює, надсилаючи to_procповідомлення на свій операнд, який у наведеному вище коді є класом Array. А оскільки я визначив #to_procметод у Array, рядок стає:
[ 'Hello', 'Goodbye' ].map { |receiver| receiver.send( :+, ' world!' ) }
Це стенограма для tags.map { |tag| tag.name }.join(' ')
&оператор викликає to_procсвій операнд. Тож це не конкретно для методу map, а насправді працює над будь-яким методом, який бере блок і передає блоку один або більше аргументів.
Відповідь Джоша Лі майже правильна, за винятком того, що еквівалентний код Ruby повинен був бути наступним.
class Symbol
def to_proc
Proc.new do |receiver|
receiver.send self
end
end
end
ні
class Symbol
def to_proc
Proc.new do |obj, *args|
obj.send self, *args
end
end
end
За допомогою цього коду, коли print [[1,'a'],[2,'b'],[3,'c']].map(&:first)він виконується, Ruby розбиває перший вхід [1,'a']на 1 і 'a', щоб дати obj1 і args*'a', щоб викликати помилку, оскільки об'єкт 1 Fixnum не має методу self (який є: першим).
Коли [[1,'a'],[2,'b'],[3,'c']].map(&:first)виконується;
:firstє об'єктом Symbol, тому, коли &:firstметод вказаний як параметр як параметр, викликається символ # to_proc.
карта надсилає повідомлення про виклик: first.to_proc з параметром [1,'a'] , наприклад, :first.to_proc.call([1,'a'])виконується.
Процедура to_proc класу Symbol надсилає повідомлення відправника об’єкту масиву ([1,'a'] ) з параметром (: перший), наприклад, [1,'a'].send(:first)виконується.
повторює решту елементів в [[1,'a'],[2,'b'],[3,'c']]об'єкті.
Це те саме, що виконувати [[1,'a'],[2,'b'],[3,'c']].map(|e| e.first)вираз.
[1,2,3,4,5,6].inject(&:+)- ін'єкція очікує лямбда з двома параметрами (пам’ятка та елемент) і :+.to_procдоставляє її - Proc.new |obj, *args| { obj.send(self, *args) }або{ |m, o| m.+(o) }
Тут відбуваються дві речі, і важливо зрозуміти обидва.
Як описано в інших відповідях, Symbol#to_procметод викликається.
Але причина to_procвикликається символом тому, що він передається mapяк блок-аргумент. Поміщення &аргументу перед викликом методу призводить до того, що він передається таким чином. Це справедливо для будь-якого методу Ruby, а не лише mapдля символів.
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)
SymbolПеретвориться до Procтому , що вона передається в якості блоку. Ми можемо показати це, намагаючись передати програму .mapбез амперсанда:
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"]
Незважаючи на те, що його не потрібно перетворювати, метод не буде знати, як його використовувати, оскільки очікує блочного аргументу. Передача його &дає .mapблок, який він очікує.
В основному виконується виклик методу tag.nameдля кожного тегу в масиві.
Це спрощена рубінова стенограма.
Хоча ми вже маємо чудові відповіді, оглядаючи перспективу новачка, я хотів би додати додаткову інформацію:
Що означає карта (&: name) у Ruby?
Це означає, що ви передаєте інший метод як параметр функції map. (Насправді ви передаєте символ, який перетворюється на процедуру. Але це не так важливо в конкретному випадку).
Важливо, що у вас є methodім’я, nameяке буде використано методом map як аргумент замість традиційного blockстилю.
По-перше, &:nameце ярлик для &:name.to_proc, де :name.to_procповертається a Proc(щось схоже, але не тотожне лямбда), яке при виклику з об'єктом як (перший) аргумент викликає nameметод на цьому об'єкті.
По- друге, в той час як &в def foo(&block) ... endзвернених блок - передається fooдо Proc, він робить протилежне при застосуванні доProc .
Таким чином, &:name.to_procце блок, який приймає об’єкт як аргумент і викликає на ньому nameметод, тобто { |o| o.name }.
Це те саме, що нижче:
def tag_names
if @tag_names
@tag_names
else
tags.map{ |t| t.name }.join(' ')
end