Назначение & (амперсанда) в Ruby для процессов и методов вызова
Я заметил, что во многих примерах, связанных с Ruby Procs, есть следующий символ&.
# Ruby Example
shout = Proc.new { puts 'Yolo!' }
def shout_n_times(n, &callback)
n.times do
callback.call
end
end
shout_n_times(3, &shout)
# prints 'Yolo!' 3 times
мой вопрос в том, какова функциональная цель символа&? Кажется, что если я написал тот же самый точный код без &, он работает так, как ожидалось:
# Same code as previous without &
shout = Proc.new { puts 'Yolo!' }
def shout_n_times(n, callback)
n.times do
callback.call
end
end
shout_n_times(3, shout)
# prints 'Yolo!' 3 times
4 ответов
в этой статье обеспечивает хороший обзор различия.
чтобы суммировать статью, Ruby позволяет неявные и явные блоки. Кроме того, у Ruby есть блок, proc и лямбда.
когда вы называете
def foo(block)
end
block
это просто аргумент метода. Аргумент ссылается на переменную block
, а как вы взаимодействуете с ним, зависит от типа объекта, который вы проходите.
def foo(one, block, two)
p one
p block.call
p two
end
foo(1, 2, 3)
1
NoMethodError: undefined method `call' for 2:Fixnum
from (irb):3:in `foo'
from (irb):6
from /Users/weppos/.rvm/rubies/ruby-2.1.5/bin/irb:11:in `<main>'
foo(1, Proc.new { 1 + 1 }, 3)
1
2
3
но когда вы используете амперсанд!--12--> в определении метода блок принимает другое значение. Ты явно определение метода для принятия блока. И другие правила будут применяться (например, не более одного блока на метод).
def foo(one, two, &block)
p one
p block.call
p two
end
прежде всего, будучи блоком, сигнатура метода теперь принимает "два параметра и блок", а не "три параметра".
foo(1, 2, Proc.new { "from the proc" })
ArgumentError: wrong number of arguments (3 for 2)
from (irb):7:in `foo'
from (irb):12
from /Users/weppos/.rvm/rubies/ruby-2.1.5/bin/irb:11:in `<main>'
это означает, что вы должны заставить третий аргумент быть блоком, передающим параметр с амперсанд.
foo(1, 2, &Proc.new { "from the proc" })
1
"from the proc"
2
однако это очень необычный синтаксис. В Ruby методы с блоками обычно называются using {}
foo(1, 2) { "from the block" }
1
"from the block"
2
или do end
.
foo(1, 2) do
"from the block"
end
1
"from the block"
2
давайте вернемся к определению метода. Ранее я упоминал, что следующий код является явное объявление блока.
def foo(one, two, &block)
block.call
end
методы могут неявно принимать блок. Неявные блоки вызываются с помощью yield
.
def foo(one, two)
p yield
end
foo(1, 2) { "from the block" }
вы можете проверить этот блок передается с помощью block_given?
def foo(one, two)
if block_given?
p yield
else
p "No block given"
end
end
foo(1, 2) { "from the block" }
=> "from the block"
foo(1, 2)
=> "No block given"
эти связанные с блоком функции не будут доступны, если вы объявите "блок" как простой аргумент (следовательно, без амперсанда), потому что это будет просто аргумент анонимного метода.
в качестве дополнения я заставляю себя вспомнить &
как знак преобразования между block
и Proc
.
преобразование block
to Proc
def foo(&p)
puts p.class
end
foo {} # => Proc
преобразование Proc
до block
def bar
yield "hello"
end
p = Proc.new {|a| puts a }
bar &p # => hello
хорошо, когда у вас есть блок, если вы примените &
перед блоком он становится Proc
объект и наоборот.
_unary &_
: это имеет какое-то отношение к преобразованию вещей в блоки и из блоков. Если вы больше ничего не забираете из этого, помните, что когда вы видите унарное "&" в Ruby, вы смотрите на то, чтобы сделать что-то в блок или сделать блок во что-то.
в первом примере в этой строке shout_n_times(3, &shout)
, вы преобразование
разница в том, что в вашем первом примере:
# Ruby Example
shout = Proc.new { puts 'Yolo!' }
def shout_n_times(n, &callback)
n.times do
callback.call
end
end
shout_n_times(3, &shout)
...синтаксис вызова метода позволяет переписать определение метода следующим образом:
shout = Proc.new { puts 'Yolo!' }
def shout_n_times(n)
n.times do
yield
end
end
shout_n_times(3, &shout)
--output:--
Yolo!
Yolo!
Yolo!
эти два утверждения:
shout = Proc.new { puts 'Yolo!' }
...
shout_n_times(3, &shout)
...эквивалентны:
shout_n_times(3) do
puts 'Yolo!'
end
и запись yield() внутри определения метода shout_n_times () вызывает блок, указанный после вызова метода:
method call +--start of block specified after the method call
| |
V V
shout_n_times(3) do
puts 'Yolo!'
end
^
|
+--end of block
вы видите, блок похож на метод, и блок получает прошло как невидимый аргумент в вызове метода, после которого Блок написал. И внутри определения метода тот, кто написал определение метода, может выполнить блок с yield (). Блоки Ruby - это не что иное, как специальный синтаксис, который позволяет передавать метод в качестве аргумента другому методу.