Почему Ruby использует выход?
Я новичок в Ruby. Я использовал много С# и JavaScript, которые позволяют выполнять функции более высокого порядка, и я обычно использую их ежедневно.
Руби кажется мне немного странным. Функция
each
может выглядеть так:
def each
@items.each do |item|
yield(item)
end
end
items.each { |item| puts item }
Тем не менее Ruby также имеет некоторую поддержку функций более высокого порядка. Вышеупомянутое может быть переписано на что-то вроде:
def each(proc)
@items.each do |item|
proc.call item
end
end
items.each -> (item) { puts item } # Or...
items.each lambda { |item| puts item }
Или даже:
def each(&proc)
@items.each do |item|
proc.call item
end
end
# No difference in syntax.
items.each { |item| puts item }
Это больше похоже на большинство других языков, и это всего лишь несколько символов дольше. Вместо явного прохождения в блоке все, кажется, использует yield
.
Сам
yield
кажется сумасшедшим, волшебным и таинственным. В конце концов, он переходит к началу вызова и захватывает блок сразу после вызова. Это кажется странным и неестественным, и я не знаю ни одной параллели этой функции на другом языке.
Итак, что сделка с yield
?
Ответы
Ответ 1
Выходные данные пропускают объекты в блок методов
[Выход] переходит в начало вызова и захватывает блок сразу после вызова.
Не совсем. yield
передает аргумент блоку; он не "захватывает блок" или ничего не делает с ним. Другими словами, это:
def foo; yield self; end
foo { |x| x.inspect }
# => "main"
Здесь yield
ничего не делает, кроме передачи аргумента блоку, который передается в метод foo
. Каждый метод Ruby поддерживает необязательный блок, за исключением случаев, когда блок действительно является обязательным, поэтому единственной "магией" является то, что язык позволяет передать блок, даже если он явно не объявлен как часть сигнатуры метода.
Дополнительные примеры
Чтобы увидеть эту неявную подпись в действии, рассмотрите следующее:
def foo; puts block_given?; end
foo { |x| x.inspect }
который выведет "true" и вернет nil
, что является ожидаемым возвращаемым значением из метода puts
.
Конечно, без yield
блок ничего не делает. Например:
def foo; end
foo { |x| x.inspect }
# => nil
Ответ 2
Доходность - это синтаксис сахара
Этот пример выхода:
def do_something_for_each(array)
array.each do |el|
yield(el)
end
end
Является просто синтаксическим сахаром для:
def do_something_for_each(array, &block)
array.each do |el|
block.call(el)
end
end
Выберите синтаксис, который вам нравится, и бегите с ним.
Ответ 3
Одним из преимуществ yield
является также использование next
(например, continue
) и break
. В других языках для next
вам может потребоваться использовать return
, а для break
вам может понадобиться (ab) использовать исключения. Возможно, лучше иметь встроенную поддержку для этих видов операций.
Ответ 4
В большинстве случаев вы выполняете блок прямо в этом методе, используя
Выход.
Блок передается прямо в метод, и тогда метод может
перейдите к блоку с ключевым словом yield.
def a_method(a, b)
a + yield(a, b)
end
a_method(1, 2) {|x, y| (x + y) * 3 } # => 10
Когда вы переходите к блоку, вы можете предоставить значения для своих аргументов,
как и при вызове метода. Кроме того, подобно методу
block возвращает результат последней строки кода, который он оценивает.