#ruby #yield #ruby-block
#ruby #выход #ruby-блок
Вопрос:
Меня устраивает следующее:
def some_def(foo, amp;block)
puts "block utilized below"
block.call(foo)
end
def some_other_def(bar)
puts "using yield below"
yield bar
puts "and back into the method"
end
Итак, я научился хранить блоки (и процедуры) отдельно от yield
ключевого слова.
Однако я столкнулся со следующим кодом:
# ./open_file.rb
class File
def self.open(name, mode, amp;block)
file = new(name, mode)
return file unless block_given?
yield(file)
ensure
file.close
end
end
Кажется, параметр amp;block
не имеет значения, когда я реализую execute этот код в irb:
irb -r ./file.open.rb
и сделайте что-то вроде:
File.open('foo.txt','r') {|f| puts f}
amp;block
Отображается необязательно block_given?
в:
return file unless block_given?
Комментарии:
1. Вы можете использовать любую форму взаимозаменяемо.
2.
amp;block
параметр всегда является необязательным параметром, с или безblock_given?
. Однако запускblock.call
(илиyield
, если уж на то пошло), когдаblock
isnil
(значение по умолчанию, когда оно не указано) приведет к сбоям; поэтому приamp;block
использовании обычно вы захотите проверитьblock_given?
. Таким образом, у вас есть случай обратной причинно-следственной связи : дело не в том, что этоblock_given?
приводитamp;block
к необязательности; дело в том, чтоamp;block
необязательность заставляет программистов использоватьblock_given?
.3. @Amadan в этом случае вы можете просто проверить через
if block
.4. @Stefan: Да, AFAIK, эти два параметра синонимичны, если есть явный параметр block . Возможно, я неправильно прочитал вопрос OP? Если OP означал »
amp;block
Делается ненужным с помощьюblock_given?
«, то здесь, в некотором смысле, ответ положительный.
Ответ №1:
Как правило, вы используете amp;block
аргумент только в том случае, если вам нужно передать блок другому методу, например, в этом составленном примере:
def m(amp;block)
some_array.map(amp;block)
end
или это реальная версия Enumerable#sum
из Rails:
def sum(identity = nil, amp;block)
if block_given?
map(amp;block).sum(identity)
else
sum = identity ? inject(identity, : ) : inject(: )
sum || identity || 0
end
end
В любом случае блок, с которым вызывается метод, используется при вызове другого метода, поэтому вам нужен способ ссылаться на блок (т.Е. имя).
Таким образом, block_given?
/ yield
и amp;block
служат разным целям. Возможность вызова block_given?
не делает amp;block
избыточным и, как в #sum
описанной выше реализации, их можно даже использовать вместе.
Комментарии:
1. Интересно, почему они не использовали
inject(identity) { |sum, e| sum yield(e) }
вместо этогоidentity
значение по умолчанию0
вместоnil
.
Ответ №2:
amp;block
В сигнатуре метода принимает блок, преобразует его в proc и присваивает его переменной с именем block
. В случае, если блок не предоставлен, он block
назначается nil
.
Использовать ли аргумент block
в определении метода, не имеет значения, точно так же, как не имеет значения, используется ли обычный аргумент метода bar
или нет в следующем определении:
def foo(bar); end
Однако принятие блока в качестве параметра и неиспользование его является избыточным и пустой тратой ресурсов. Возможно, все еще имеет смысл явно указать коллеге-программисту, что метод принимает блок.
Использование block_given?
не зависит от всего этого. Это не зависит от того, был ли принят блок в качестве аргумента через amp;
. Он ссылается на блок напрямую, независимо от block
.