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

"за" против "каждый" в Ruby

У меня просто был быстрый вопрос относительно петель в Ruby. Есть ли разница между этими двумя способами итерации через коллекцию?

# way 1
@collection.each do |item|
  # do whatever
end

# way 2
for item in @collection
  # do whatever
end

Просто интересно, совпадают ли они или, может быть, есть тонкая разница (возможно, когда @collection равно нулю).

4b9b3361

Ответ 1

Это единственное отличие:

каждый:

irb> [1,2,3].each { |x| }
  => [1, 2, 3]
irb> x
NameError: undefined local variable or method `x' for main:Object
    from (irb):2
    from :0

для

irb> for x in [1,2,3]; end
  => [1, 2, 3]
irb> x
  => 3

В цикле for переменная итератора сохраняется после завершения блока. В цикле each это не так, если только он не был определен как локальная переменная до начала цикла.

Кроме того, for является просто синтаксическим сахаром для метода each.

Когда @collection равно nil, оба цикла генерируют исключение:

Исключение: undefined локальная переменная или метод `@collection 'для main: Object

Ответ 3

Ваш первый пример,

@collection.each do |item|
  # do whatever
end

более идиоматично. Хотя Ruby поддерживает конструкции цикла, такие как for и while, предпочтительным является синтаксис блока.

Другое тонкое различие заключается в том, что любая переменная, которую вы объявляете в цикле for, будет доступна вне цикла, тогда как те, которые находятся внутри блока итератора, являются фактически частными.

Ответ 5

Похоже, что нет разницы, for использует each под ним.

$ irb
>> for x in nil
>> puts x
>> end
NoMethodError: undefined method `each' for nil:NilClass
    from (irb):1
>> nil.each {|x| puts x}
NoMethodError: undefined method `each' for nil:NilClass
    from (irb):4

Как говорит Баярд, каждый из них более идиоматичен. Он больше скрывает от вас и не требует специальных языковых функций. Комментарий Telemachus

for .. in .. устанавливает итератор за пределы области цикла, поэтому

for a in [1,2]
  puts a
end

оставляет a, определенный после завершения цикла. Где в качестве each нет. Это еще одна причина в пользу использования each, потому что временная переменная живет короче.

Ответ 6

Никогда не используйте for, это может вызвать ошибки.

Разница тонкая, но может вызвать огромные ошибки!

Не обманывайте себя, это не касается идиоматических ошибок кода или стиля. Это вопрос избежания почти необъяснимых ошибок в производственном коде. Ruby-реализация for имеет серьезный недостаток и не должна использоваться. Всегда используйте циклы each, никогда не используйте цикл for.

Вот пример, где for вводит ошибку,

class Library
  def initialize
    @ary = []
  end
  def method_with_block(&block)
    @ary << block
  end
  def method_that_uses_these_blocks
    @ary.map(&:call)
  end
end

lib = Library.new

for n in %w{foo bar quz}
  lib.method_with_block { n }
end

puts lib.method_that_uses_these_blocks

Печать

quz
quz
quz

Использование %w{foo bar quz}.each { |n| ... } prints

foo
bar
quz

Почему?

В цикле for переменная n определяется один раз и только, а затем это одно определение используется для всех итераций. Следовательно, каждый блок ссылается на тот же n, который имеет значение quz к завершению цикла. Ошибка!

В цикле each для каждой итерации определена новая переменная n, например над переменной n определяется три отдельных раза. Следовательно, каждый блок ссылается на отдельный n с правильными значениями.

Ответ 7

Насколько я знаю, использование блоков вместо структур управления на языке более идиоматично.

Ответ 8

Я просто хочу особо отметить цикл for in в Ruby. Может показаться, что это конструкция, похожая на другие языки, но на самом деле это выражение, похожее на все другие циклические конструкции в Ruby. Фактически, for in работает с объектами Enumerable так же, как и каждый итератор.

Коллекция, передаваемая для in, может быть любым объектом, у которого есть метод каждого итератора. Массивы и хэши определяют каждый метод, как и многие другие объекты Ruby. Цикл for/in вызывает каждый метод указанного объекта. Поскольку этот итератор выдает значения, цикл for присваивает каждое значение (или каждый набор значений) указанной переменной (или переменным), а затем выполняет код в теле.

Это глупый пример, но он иллюстрирует тот факт, что цикл for in работает с ЛЮБЫМ объектом, который имеет каждый метод, так же, как и каждый итератор:

class Apple
  TYPES = %w(red green yellow)
  def each
    yield TYPES.pop until TYPES.empty?
  end
end

a = Apple.new
for i in a do
  puts i
end
yellow
green
red
=> nil

А теперь каждый итератор:

a = Apple.new
a.each do |i|
  puts i
end
yellow
green
red
=> nil

Как вы можете видеть, оба отвечают на каждый метод, который возвращает значения обратно в блок. Как все здесь заявили, определенно предпочтительнее использовать каждый итератор, чем цикл in. Я просто хотел показать, что в цикле for нет ничего волшебного. Это выражение, которое вызывает каждый метод коллекции, а затем передает его в свой блок кода. Следовательно, это очень редкий случай, который вам нужно использовать для in. Используйте каждый итератор почти всегда (с дополнительным преимуществом области видимости блока).

Ответ 9

(1..4).each { |i| 


  a = 9 if i==3

  puts a 


}
#nil
#nil
#9
#nil

for i in 1..4

  a = 9 if i==3

  puts a

end
#nil
#nil
#9
#9

В цикле "for" локальная переменная все еще живет после каждого цикла. В "каждом" цикле локальная переменная обновляется после каждого цикла.