Рубин для петли ловушка?

В обсуждении Ruby-циклов, Никлас Б. недавно говорил о цикле «не вводить новую область видимости» по сравнению с каждым циклом. Я хотел бы увидеть несколько примеров того, как это чувствуется.

O.K., Я расширяю вопрос: где еще в Ruby мы видим, что apears делают / заканчивают разделители блоков, но внутри фактически нет области видимости? Что-нибудь еще, кроме ... сделать ... конец?

O.K., Еще одно расширение вопроса, есть ли способ написать цикл с фигурными скобками {блок}?

 Niklas B.01 мая 2012 г., 12:40
А как же наоборот? Вы приводите реальные примеры того, как вы думаете,for цикл нужен, и мы дадим вам лучшую альтернативу? :) В противном случае я думаю, что этот вопрос просто дубликат Stackoverflow.com / вопросы / 3294509 / за-против-каждого-в-рубин
 Niklas B.01 мая 2012 г., 13:07
Обычно вы должны заботиться о читабельности гораздо больше, чем о производительности, иначе зачем вам в первую очередь использовать Ruby. Определение переменной внутри цикла для последующего использования пахнет очень плохим дизайном.
 Boris Stitnicky01 мая 2012 г., 13:01
Как насчет производительности? Разве не проще, чем войти в замыкание? А что если тыхоч определить локальные переменные в цикле for для последующего использования :)? (Просто быстро подумайте о том, что может быть полезно для:)
 Niklas B.01 мая 2012 г., 12:38
Просто примечание (я уже говорил что-то подобное в другом комментарии): проблема сfor не только незначительно отличается от других конструкций, использующихdo/end, но также и то, что он может использоваться только по одной причине: обход перечислимого. Часто конструкции из функционального программирования, такие какselect илиmap гораздо лучше подходит для выражения проблемы, чем простой старый цикл, унаследованный непосредственно от C.

Ответы на вопрос(3)

даже блоки не идеальны в Ruby до 1.9. Они не всегда вводят новую область видимости:

i = 0
results = []
(1..3).each do |i|
  results << lambda { i }
end
i = 5
p results.map(&:call)  # => [5,5,5]
 Niklas B.01 мая 2012 г., 16:03
В Ruby 1.9 это должно привести к[1,2,3], как и ожидалось (также, это может вызвать предупреждение о затененной переменной).
 Niklas B.01 мая 2012 г., 16:19
Я добавил тот факт, что это относится только к 1,8 к вашему ответу. Надеюсь, ты не против.
 Niklas B.01 мая 2012 г., 16:15
Я думаю, что в ранних версиях 1.9 было предупреждение об этом, чтобы пользователи устаревшего кода знали об этой проблеме. Похоже, что он будет удален в более поздних версиях.
 Victor Moroz01 мая 2012 г., 16:14
@ NiklasB. Спасибо, приятно знать, что это действительно слабое место R1.8, но в R1.9 предупреждений нет, хотя, между прочим. Просто любопытно, как это работает с устаревшим кодом, я помню, что это был серьезный момент, когда он обсуждался для 1.9
 Jon Wingfield18 авг. 2012 г., 14:39
Может, идиот, но мне действительно не нравится использование фразы «не вводите новый объе. "Они вводят новыйобъе, но на переменную ссылаются во внешней области видимости. Поскольку блоки закрываются над Переменные незначени, вы получите любое значениеi это когда лямбданазываетс, а не когда это Определено.
Решение Вопроса

Давайте проиллюстрируем это на примере:

results = []
(1..3).each do |i|
  results << lambda { i }
end
p results.map(&:call)  # => [1,2,3]

Ох, это то, что ожидалось. Теперь проверьте следующее:

results = []
for i in 1..3
  results << lambda { i }
end
p results.map(&:call)  # => [3,3,3]

А что происходит? Поверьте мне, такого рода ошибки противно выслеживать. Разработчики Python или JS поймут, что я имею в виду

Это одна из причин, по которой я избегаю таких петель, как чума, хотя есть и более веские аргументы в пользу этой позиции. Как правильно указал Бен, используя правильный метод изEnumerable почти всегда приводит к лучшему коду, чем использование простого старого, императивногоfor петли или любительEnumerable#each. Например, приведенный выше пример также может быть кратко записан как

lambdas = 1.upto(3).map { |i| lambda { i } }
p lambdas.map(&:call)

Я расширяю вопрос: где еще в Ruby мы видим, что apears делают / заканчивают разделители блоков, но внутри фактически нет области видимости? Что-нибудь еще, кроме ... сделать ... конец?

Каждый из циклических конструкций может быть использован таким образом:

while true do
  #...
end

until false do
  # ...
end

С другой стороны, мы можем написать все безdo (что явно предпочтительнее):

for i in 1..3
end

while true
end

until false
end

Еще одно расширение вопроса, есть ли способ написать цикл с фигурными скобками {блок}

Нет, нет. Также обратите внимание, что термин «блок» имеет особое значение в Ruby.

 Ben Kreeger01 мая 2012 г., 17:04
А, я вижу, у тебя есть! Спасибо, что отвезли его домой.
 Ben Kreeger01 мая 2012 г., 15:43
Я также подробно остановлюсь на этом (отличный ответ, Никлас Б.). Никогда не говори никогда, но яникогд пиши и не хочешь видетьfor циклы в коде Ruby. Они совсем не идиоматичны и вводят неприятные ошибки для неосведомленных разработчиков. Используйте что-то, предоставленноеEnumerable вместо.
 Niklas B.01 мая 2012 г., 16:04
@ Ben: Конечно, я точно придерживаюсь вашего мнения, и я ясно дал понять, что в обсуждении OP ссылается на:)
 Boris Stitnicky01 мая 2012 г., 18:15
@ NiklasB .: Теперь, основываясь на этом, порекомендуете ли вы просто использовать цикл for, а циклы «до» и «до» также не рекомендуется для серьезного использования?
 Niklas B.01 мая 2012 г., 18:19
@ Борис: Да, часто вы можете избежать их использования. Однако у них нет очевидной альтернативы, в отличие отfor, так что вам нужно немного подумать, чтобы заменить их более совершенными конструкциями или прийти к выводу, что они на самом деле являются лучшим решением конкретной проблемы.

for, а потом объясни, почему ты мог.

Основная причина, по которой ты не хотел бы использоватьfor в том, что это не идиоматично. Если вы используетеeach, вы можете легко заменить этоeach сmap илиfind илиeach_with_index без существенного изменения вашего кода. Но нетfor_map илиfor_find илиfor_with_index.

Другая причина в том, что если вы создаете переменную внутри блока внутриeach, и он не был создан ранее, он будет существовать только до тех пор, пока существует этот цикл. Избавление от переменных, когда вы их не используете, - это хорошо.

Теперь я расскажу, почему вы можете использоватьfor. each создает замыкание для каждого цикла, и если вы повторяете этот цикл слишком много раз, этот цикл может вызвать проблемы с производительностью. Вhttps: //stackoverflow.com/a/10325493/3876, Я опубликовал это, используяwhile цикл, а не блок сделал его медленнее.

RUN_COUNT = 10_000_000
FIRST_STRING = "Woooooha"
SECOND_STRING = "Woooooha"

def times_double_equal_sign
  RUN_COUNT.times do |i|
    FIRST_STRING == SECOND_STRING
  end
end

def loop_double_equal_sign
  i = 0
  while i < RUN_COUNT
    FIRST_STRING == SECOND_STRING
    i += 1
  end
end

times_double_equal_sign постоянно занимал 2,4 секунды, аloop_double_equal_sign был постоянно на 0,2-0,3 секунды быстрее.

Вhttps: //stackoverflow.com/a/6475413/3876, Я обнаружил, что выполнение пустого цикла заняло 1,9 секунды, тогда как выполнение пустого блока заняло 5,7 секунды.

Знай, почему ты не хотел бы использоватьfor, знаешь, почему ты хочешь использоватьfor, и используйте последний, только когда это необходимо. Если вы не испытываете ностальгию по другим языкам. :)

Ваш ответ на вопрос