Есть ли в Ruby цикл "do ... while"? - PullRequest
439 голосов
/ 26 сентября 2008

Я использую этот код, чтобы позволить пользователю вводить имена, в то время как программа сохраняет их в массиве, пока они не введут пустую строку (они должны нажимать ввод после каждого имени):

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. Кто-нибудь может предложить лучший способ сделать это?

Ответы [ 10 ]

617 голосов
/ 23 мая 2012

ВНИМАНИЕ

begin <code> end while <condition> отклонено автором Ruby Matz. Вместо этого он предлагает использовать 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 wiki имеет похожую историю:

В ноябре 2005 года Юкихиро Мацумото, создатель Ruby, пожалел об этой функции цикла и предложил использовать Kernel # loop.

186 голосов
/ 26 сентября 2008

Я нашел следующий фрагмент при чтении исходного кода для 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
?>   puts "do {} while ()" 
>> end while false
do {} while ()
=> nil

Как и следовало ожидать, цикл продолжит выполняться, пока модификатор имеет значение true.

>> n = 3
=> 3
>> begin
?>   puts n
>>   n -= 1
>> end while n > 0
3
2
1
=> nil

Хотя я был бы рад никогда больше не видеть эту идиому, начни ... конец довольно силен. Ниже приводится распространенная идиома для запоминания однострочного метода без параметров:

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

Первоначально написано Джереми Вурхис . Содержание было скопировано здесь, потому что, кажется, оно было удалено с исходного сайта. Копии также можно найти в Веб-архиве и на Ruby Buzz Forum . Билл Ящерица

100 голосов
/ 26 сентября 2008

Как это:

people = []

begin
  info = gets.chomp
  people += [Person.new(info)] if not info.empty?
end while not info.empty?

Ссылка: Ruby's Hidden do {} while () Loop

43 голосов
/ 26 сентября 2008

Как насчет этого?

people = []

until (info = gets.chomp).empty?
  people += [Person.new(info)]
end
11 голосов
/ 22 июля 2010

Вот полный текст статьи из мертвой ссылки Хаббардра на мой блог.

Я нашел следующий фрагмент при чтении источника для 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

Как и следовало ожидать, цикл продолжит выполняться, пока модификатор имеет значение true.

>> 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
8 голосов
/ 17 февраля 2013

Теперь это работает правильно:

begin
    # statment
end until <condition>

Но это может быть удалено в будущем, потому что утверждение begin нелогично. Смотри: http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-core/6745

Мац рекомендовал сделать это так:

loop do
    # ...
    break if <condition>
end
5 голосов
/ 28 августа 2013

Из того, что я понял, Мацу не нравится конструкция

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

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

3 голосов
/ 05 августа 2010
a = 1
while true
  puts a
  a += 1
  break if a > 10
end
2 голосов
/ 10 ноября 2011

Вот еще один:

people = []
1.times do
  info = gets.chomp
  unless info.empty? 
    people += [Person.new(info)]
    redo
  end
end
0 голосов
/ 11 января 2010
ppl = []
while (input=gets.chomp)
 if !input.empty?
  ppl << input
 else
 p ppl; puts "Goodbye"; break
 end
end
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...