Я нашел этот код в 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методом, то вы можете передать его методу as &foo, который будет вызывать foo.to_procи использовать его как блок метода.
Symbol#to_procМетод был первоначально добавлен ActiveSupport , но был интегрирован в Руби 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 метод, который преобразует его в 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метод на массиве, строка становится:
[ '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', чтобы вызвать ошибку, так как объект Fixnum 1 не имеет метода self (который: first).
Когда [[1,'a'],[2,'b'],[3,'c']].map(&:first)выполняется;
:firstявляется объектом Symbol, поэтому когда &:firstон передается методу карты в качестве параметра, вызывается Symbol # to_proc.
Карта отправляет сообщение о вызове: first.to_proc с параметром [1,'a'], например, :first.to_proc.call([1,'a'])выполняется.
Процедура to_proc в классе Symbol отправляет сообщение отправки в объект массива ( [1,'a']) с параметром (: first), например, [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которое будет использоваться методом карты в качестве аргумента вместо традиционного blockстиля.
Во-первых, &:nameэто ярлык для &:name.to_proc, где :name.to_procвозвращает Proc(что-то похожее, но не идентичное лямбда-выражению), которое при вызове с объектом в качестве (первого) аргумента вызывает nameметод для этого объекта.
Во-вторых, в то время как &in def foo(&block) ... endпреобразует блок, переданный в fooa Proc, он делает обратное при применении к a Proc.
Таким образом, &:name.to_procэто блок , который принимает объект в качестве аргумента и вызывает nameметод на него, то есть { |o| o.name }.
Это так же, как показано ниже:
def tag_names
if @tag_names
@tag_names
else
tags.map{ |t| t.name }.join(' ')
end