Как уже указывалось, основная проблема заключается в том, что при попытке передать функцию в качестве параметра Ruby выполняет ее - в качестве побочного эффекта от скобок, являющегося необязательным.
Мне понравилась простота метода символов, о котором упоминалось ранее, но я боялся бы, что в будущем я сам забуду, что для передачи этой работы нужно передать итератор в качестве символа. Когда читаемость является желаемой функцией, вы можете заключить свой итератор в объект, который вы можете передавать, не опасаясь неожиданного выполнения кода.
Анонимный объект как итератор
То есть: использование анонимного объекта с одной функцией в качестве итератора. Довольно быстро, чтобы читать и понимать. Но из-за ограничений в способе работы Ruby с областью действия итератор не может легко получить параметры: любые параметры, полученные в функции iterator
, не будут автоматически доступны в each
.
def iterator
def each
yield("Value 1")
yield("Value 2")
yield("Value 3")
end
end
def iterate(my_iterator)
my_iterator.each do |value|
puts value
end
end
iterate iterator
Proc объект как итератор
Использование объекта Proc
в качестве итератора позволяет легко использовать любые переменные, переданные в конструктор итератор . Темная сторона: это начинает выглядеть странно. Чтение блока Proc.new
не является незамеченным для неопытного глаза. Кроме того: неспособность использовать yield
делает это немного уродливым ИМХО.
def iterator(prefix:)
Proc.new { |&block|
block.call("#{prefix} Value 1")
block.call("#{prefix} Value 2")
block.call("#{prefix} Value 3")
}
end
def iterate(my_iterator)
my_iterator.call do |value|
puts value
end
end
iterate iterator(prefix: 'The')
Лямбда в качестве итератора
Идеально, если вы хотите запутать свой код так сильно, чтобы никто, кроме вас, не мог его прочитать.
def iterator(prefix:)
-> (&block) {
block.call("#{prefix} Value 1")
block.call("#{prefix} Value 2")
block.call("#{prefix} Value 3")
}
end
def iterate(my_iterator)
my_iterator.call do |value|
puts value
end
end
iterate iterator(prefix: 'The')
Класс как итератор
И, наконец, хороший старый подход. Немного многословно для инициализации на мой вкус, но с небольшим или вообще без неожиданного эффекта.
class Iterator
def initialize(prefix:)
@prefix = prefix
end
def each
yield("#{@prefix} Value 1")
yield("#{@prefix} Value 2")
yield("#{@prefix} Value 3")
end
end
def iterate(my_iterator)
my_iterator.each do |value|
puts value
end
end
iterate Iterator.new(prefix: 'The')