Есть ли цикл "do... while" в Ruby?

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

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 loop:

people = []

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

В этом коде мне не нужно присваивать информацию некоторой случайной строке.

К сожалению, этот тип цикла, похоже, не существует в Ruby. Может ли кто-нибудь предложить лучший способ сделать это?

Ответ 1

ВНИМАНИЕ:

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 possible.
|
|I'm surprised.  What do you regret about it?

Because it hard for users to tell

  begin <code> end while <cond>

works differently from

  <code> while <cond>

RosettaCode wiki имеет похожую историю:

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

Ответ 2

  При чтении исходного кода для 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

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

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. -Bill Ящерица

Ответ 3

Как это:

people = []

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

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

Ответ 4

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

people = []

until (info = gets.chomp).empty?
  people += [Person.new(info)]
end

Ответ 5

Здесь полная текстовая статья из 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 достаточно силен. Ниже приведена общая идиома для memoize однострочного метода без параметров:

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

Ответ 6

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

begin
    # statment
end until <condition>

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

Matz (Rubys Creator) рекомендовал сделать это следующим образом:

loop do
    # ...
    break if <condition>
end

Ответ 7

Из того, что я собираю, Matz не нравится конструкция

begin
    <multiple_lines_of_code>
end while <cond>

потому что эта семантика отличается от

<single_line_of_code> while <cond>

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

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

Ответ 8

a = 1
while true
  puts a
  a += 1
  break if a > 10
end

Ответ 9

Здесь еще один:

people = []
1.times do
  info = gets.chomp
  unless info.empty? 
    people += [Person.new(info)]
    redo
  end
end

Ответ 10

ppl = []
while (input=gets.chomp)
 if !input.empty?
  ppl << input
 else
 p ppl; puts "Goodbye"; break
 end
end