Я использую этот код, чтобы позволить пользователю вводить имена, в то время как программа хранит их в массиве, пока они не введут пустую строку (они должны нажимать ввод после каждого имени):
people = []
info = 'a' # must fill variable with something, otherwise loop won't execute
while not info.empty?
info = gets.chomp
people += [Person.new(info)] if not info.empty?
end
Этот код выглядел бы намного лучше в цикле do ... while:
people = []
do
info = gets.chomp
people += [Person.new(info)] if not info.empty?
while not info.empty?
В этом коде мне не нужно назначать информацию какой-то случайной строке.
К сожалению, такого типа циклов в Ruby не существует. Кто-нибудь может предложить лучший способ сделать это?
@ Джереми Рутен, есть ли шанс, что вам будет интересно изменить принятый ответ на ответ Сивэя Шена, loop do; ...; break if ...; end?

I found the following snippet while reading the source for
Tempfile#initializein the Ruby core library:begin tmpname = File.join(tmpdir, make_tmpname(basename, n)) lock = tmpname + '.lock' n += 1 end while @@cleanlist.include?(tmpname) or File.exist?(lock) or File.exist?(tmpname)At first glance, I assumed the while modifier would be evaluated before the contents of begin...end, but that is not the case. Observe:
>> begin ?> puts "do {} while ()" >> end while false do {} while () => nilAs you would expect, the loop will continue to execute while the modifier is true.
>> n = 3 => 3 >> begin ?> puts n >> n -= 1 >> end while n > 0 3 2 1 => nilWhile I would be happy to never see this idiom again, begin...end is quite powerful. The following is a common idiom to memoize a one-liner method with no params:
def expensive @expensive ||= 2 + 2 endHere is an ugly, but quick way to memoize something more complex:
def expensive @expensive ||= begin n = 99 buf = "" begin buf << "#{n} bottles of beer on the wall\n" # ... n -= 1 end while n > 0 buf << "no more bottles of beer" end end
Первоначально написано Джереми Вурхис. Контент был скопирован сюда, потому что, похоже, он был удален с исходного сайта. Копии также можно найти в Веб-архив и Ruby Buzz Forum. -Билл Ящерица
Предоставлено Wayback Machine: web.archive.org/web/20080206125158/http://archive.jvoorhis.c om /…
Вот почему, ссылаясь на внешний сайт, я всегда стараюсь скопировать соответствующую информацию в свой ответ здесь.
Почему вы ожидаете, что модификатор while будет оцениваться до содержимого begin ... end? Так делают .. циклы пока должны работать. И с чего бы вам «быть счастливым, никогда больше не увидеть эту идиому»? Что с этим не так? Я запутался.
begin ... end выглядит как блок, аналогично {...}. В этом нет ничего плохого.
-1: Ответ Сивэй Шена объясняет, что begin .. end несколько осуждается. Вместо этого используйте loop do .. break if <condition>.
@choonkeat использовать в то время как? Я действительно не понимаю, что не так с обычным языком в других языках делать, пока выражение.
@Fedcomp извините, по сравнению с другими языками является довольно спорным. Дело в том, что Ruby несовместим с самим собой, поэтому разработчик языка сожалеет об этом. Я расширил суть, чтобы уточнить, что означает "несогласованный" gist.github.com/choonkeat/9815130
Довольно мило, как вы набрали 176 голосов за ответ Джереми Вурхиса!
@ Джейкоб, я знаю: | Изначально это была просто ссылка, но, как отмечают комментаторы, она была удалена, поэтому редактор изменил сообщение и скопировал его содержимое. Это не то, что я изначально опубликовал. :)
Нравится:
people = []
begin
info = gets.chomp
people += [Person.new(info)] if not info.empty?
end while not info.empty?
Ссылка: Скрытый цикл Ruby do {} while ()
Не добавит ли этот код пустую строку в массив, если нет ввода?
Хотя здесь это не применяется, одна проблема конструкции begin-end-while заключается в том, что, в отличие от всех других конструкций ruby, она не возвращает значение последнего выражения: «begin 1 end while false» возвращает nil (не 1, не ложь)
Вы можете использовать until info.empty?, а не while not info.empty?.
На самом деле @AndrewR это своего рода точка ... сделать что-то перед сравнением ... отобразить ("Остающийся = # {счетчик}) пока (счетчик> 0) ... Я получаю отображение" Осталось = 0 ".. идеально!
Как насчет этого?
people = []
until (info = gets.chomp).empty?
people += [Person.new(info)]
end
Но это не цикл «делать ... пока». :)
Но то же самое и в этом случае, если я не ошибаюсь
@Blorgbeard, цикл do..time всегда выполняется один раз, а затем оценивает, следует ли ему продолжать работу. Традиционный цикл while / until может выполняться 0 раз. Это не ОГРОМНАЯ разница, но они разные.
@Scott, это правда - я просто имел в виду, что этот код эквивалентен OP, даже если он не использует do / while. Хотя на самом деле этот код выполняет половину «работы» цикла в условии, так что это тоже не совсем традиционный цикл while - если условие не совпадает, некоторая работа все еще выполняется.
Просто попробовал это. У вас может быть блок в форме начинаться ... заканчиваться до. Здорово!
Это цикл while, а не do ... while.
Нет цикла do while ... это простое время do.
Вопрос был в том, может ли кто-нибудь предложить лучший способ написания кода, который не обязательно требует времени. Поскольку это элегантно отвечает на вопрос, +1.
ppl = []
while (input=gets.chomp)
if !input.empty?
ppl << input
else
p ppl; puts "Goodbye"; break
end
end
это немного похоже на goto. Код скрывает ваше намерение и выглядит очень неубедительным.
запутывать * не запутывать.
Вот полный текст статьи с мертвой ссылки Hubbardr на мой блог.
При чтении исходного кода Tempfile#initialize в основной библиотеке Ruby я нашел следующий фрагмент:
begin
tmpname = File.join(tmpdir, make_tmpname(basename, n))
lock = tmpname + '.lock'
n += 1
end while @@cleanlist.include?(tmpname) or
File.exist?(lock) or File.exist?(tmpname)
На первый взгляд я предполагал, что модификатор while будет оцениваться раньше, чем содержимое begin...end, но это не так. Наблюдать:
>> begin
?> puts "do {} while ()"
>> end while false
do {} while ()
=> nil
Как и следовало ожидать, цикл будет продолжать выполняться, пока модификатор истинен.
>> n = 3
=> 3
>> begin
?> puts n
>> n -= 1
>> end while n > 0
3
2
1
=> nil
Хотя я был бы счастлив больше никогда не увидеть эту идиому, begin...end довольно мощный. Ниже приводится распространенная идиома для запоминания однострочного метода без параметров:
def expensive
@expensive ||= 2 + 2
end
Вот уродливый, но быстрый способ запомнить что-то более сложное:
def expensive
@expensive ||=
begin
n = 99
buf = ""
begin
buf << "#{n} bottles of beer on the wall\n"
# ...
n -= 1
end while n > 0
buf << "no more bottles of beer"
end
end
a = 1
while true
puts a
a += 1
break if a > 10
end
это немного похоже на переход к. Код скрывает ваше намерение.
Выглядит отлично, за исключением того, что while true можно заменить на loop do.
@DavidWiniecki, действительно, while true можно заменить на loop do. Но я протестировал обе конструкции с большим количеством итераций внутри цикла и обнаружил, что while true как минимум в 2 раза больше Быстрее, чем loop do. Не могу объяснить разницу, но она определенно есть. (Обнаружено во время тестирования Advent of Code 2017, день 15.)
Вот еще один:
people = []
1.times do
info = gets.chomp
unless info.empty?
people += [Person.new(info)]
redo
end
end
Я предпочитаю это, так как unless находится прямо впереди, и я не читаю кучу кода (которого может быть больше, чем показано здесь) только для того, чтобы найти «болтающийся» unless в конце. Общий принцип кода состоит в том, что модификаторы и условия легче использовать, если они расположены так «впереди».
Иногда мне хочется, чтобы мы, программисты, платили наличными за каждое дополнительное сравнение. И то, как он «выглядит» для нас, менее важно, чем то, как он выглядит для того, что использует его миллион раз в день.
ОСТОРОЖНОСТЬ:
begin <code> end while <condition> отклонен автором Ruby Матцем. Вместо этого он предлагает использовать Kernel#loop, например.
loop do
# some code here
break if <condition>
end
Вот обмен электронной почтой от 23 ноября 2005 г., где Матц заявляет:
|> Don't use it please. I'm regretting this feature, and I'd like to
|> remove it in the future if it's possible.
|
|I'm surprised. What do you regret about it?
Because it's hard for users to tell
begin <code> end while <cond>
works differently from
<code> while <cond>
У RosettaCode вики похожая история:
During November 2005, Yukihiro Matsumoto, the creator of Ruby, regretted this loop feature and suggested using Kernel#loop.
Пятно на. Этот метод begin end while казался неправильным. Спасибо, что дали мне корм, в котором я нуждался, чтобы убедить остальную команду.
Похоже, что цикл begin-end-while фактически оценивает условие перед запуском цикла. Разница между этим и обычным циклом while заключается в том, что он гарантированно запускается хотя бы один раз. Этого достаточно, чтобы сделать ... и вызвать проблемы.
Итак, насколько я хорошо понял, begin-end-while «сожалеет», потому что нарушает семантику модификаторов, то есть: они проверяются перед выполнением блока, например: кладет k if! K.nil ?. Здесь if - это модификатор: он проверяется ПЕРЕД, чтобы определить, выполняется ли выполнение оператора put k. Это не тот случай, когда while / until циклически повторяется (при использовании в качестве модификаторов блока begin-end !), оцениваются ПОСЛЕ первого выполнения. Может быть, это вызвало сожаление, но есть ли у нас что-то «сильнее», чем старый пост на форуме, своего рода официальное осуждение, как это обычно бывает во многих других случаях?
Это объяснение, которое я искал, когда задавался вопросом, почему для этого есть проверка Rubocop (Lint / Loop) github.com/bbatsov/rubocop/blob/v0.34.2/lib/rubocop/cop/lint /…
Мне потребовалось унизительное количество времени, чтобы понять, почему я не использовал этот рубиновый цикл do-while. Вы должны использовать 'if', чтобы более точно имитировать do-while в стиле c, иначе вы можете закончить, как я, и забыть инвертировать условие: p
В моих Ruby 2.2.3 и 1.9.3 состояние while оценивается после выполнения цикла begin-end. Посмотрите этот фрагмент кода: repl.it/CDJE/0
Я не понимаю, зачем ему добавлять его в язык, если он не согласен с его использованием?
@James Согласно связанному письму, он сказал, что "сожалеет" о его добавлении. Иногда люди совершают ошибки, даже если они разработчики языков.
Теперь это работает правильно:
begin
# statment
end until <condition>
Но в будущем он может быть удален, потому что утверждение begin противоречит здравому смыслу. См .: http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-core/6745
Мац (создатель Ruby) рекомендовал сделать это следующим образом:
loop do
# ...
break if <condition>
end
Насколько я понимаю, Мацу не нравится конструкция
begin
<multiple_lines_of_code>
end while <cond>
потому что его семантика отличается от
<single_line_of_code> while <cond>
в том, что первая конструкция сначала выполняет код перед проверкой условия, а вторая конструкция сначала проверяет условие перед выполнением кода (если когда-либо). Я так понимаю, Матц предпочитает оставить вторую конструкцию, потому что она соответствует одной строчной конструкции операторов if.
Мне никогда не нравилась вторая конструкция даже для операторов if. Во всех остальных случаях компьютер выполняет код слева направо (например, || и &&) сверху вниз. Люди читают код слева направо сверху донизу.
Вместо этого я предлагаю следующие конструкции:
if <cond> then <one_line_code> # matches case-when-then statement
while <cond> then <one_line_code>
<one_line_code> while <cond>
begin <multiple_line_code> end while <cond> # or something similar but left-to-right
Я не знаю, будут ли эти предложения соответствовать остальному языку. Но в любом случае Я предпочитаю сохранять исполнение слева направо, а также согласованность языка.
Я думаю, что обычный цикл while выглядит лучше и его легче читать.