"за" против "каждого" в рубине - PullRequest
189 голосов
/ 21 июля 2010

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

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

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

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

Ответы [ 9 ]

300 голосов
/ 21 июля 2010

Это единственная разница:

каждый:

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, оба цикла генерируют исключение:

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

43 голосов
/ 21 июля 2010

См. " Зло For For Loop " для хорошего объяснения (есть одно небольшое отличие, учитывая переменную область видимости).

Использование each считается более идиоматичным использование Ruby.

27 голосов
/ 21 июля 2010

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

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

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

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

6 голосов
/ 02 августа 2012

Еще один другой ..

number = ["one", "two", "three"]
 => ["one", "two", "three"] 

loop1 = []
loop2 = []

number.each do |c|
  loop1 << Proc.new { puts c }
end
 => ["one", "two", "three"] 

for c in number
  loop2 << Proc.new { puts c }
end
 => ["one", "two", "three"] 

loop1[1].call
two
 => nil 

loop2[1].call
three
 => nil 

источник: http://paulphilippov.com/articles/enumerable-each-vs-for-loops-in-ruby

для большей ясности: http://www.ruby -forum.com / topic / 179264 # 784884

2 голосов
/ 21 июля 2010

Похоже, что нет никакой разницы, 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

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

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

for a in [1,2]
  puts a
end

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

1 голос
/ 24 декабря 2016

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

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

Не обманывайте себя, речь не идет о проблемах с идиоматическим кодом или стилем. Это вопрос избежания почти не поддающихся отслеживанию ошибок в рабочем коде. Реализация for в Ruby имеет серьезный недостаток и не должна использоваться. Всегда используйте петли 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| ... } отпечатков

foo
bar
quz

Почему?

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

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

0 голосов
/ 16 апреля 2019
(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' локальная переменная все еще живет после каждого цикла.В «каждом» цикле локальная переменная обновляется после каждого цикла.

0 голосов
/ 12 марта 2019

Я просто хочу особо отметить цикл 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. Используйте каждый итератор почти всегда (с дополнительным преимуществом области видимости блока).

0 голосов
/ 21 июля 2010

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

...