Подтвердить что ты не робот

Почему 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?

4b9b3361

Ответ 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 возвращает результат последней строки кода, который он оценивает.