web-dev-qa-db-ja.com

Rubyに "do ... while"ループはありますか?

私はこのコードを使用して、ユーザーが名前を入力できるようにしながら、プログラムが空の文字列を入力するまでそれらを配列に格納するようにします(各名前の後で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ループではもっと見栄えがよくなります。

people = []

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

このコードでは、ランダムな文字列に情報を割り当てる必要はありません。

残念ながら、この種のループはRubyには存在しないようです。誰かがこれを行うためのより良い方法を提案できますか?

440
Jeremy Ruten

注意

begin <code> end while <condition>はRubyの作者Matzによって拒否されました。代わりに、彼はKernel#loopを使うことを提案します。

loop do 
  # some code here
  break if <condition>
end 

これが 2005年11月23日のMatz氏の電子メール交換 です。

|> 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年11月、Rubyの作成者である松本幸宏氏は、このループ機能を後悔し、Kernel#loopの使用を提案しました。

620

RubyコアライブラリのTempfile#initializeのソースを読みながら、私は次のスニペットを見つけました:

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

もともと Jeremy Voorhis によって書かれました。元のサイトから削除されたと思われるため、ここにコンテンツをコピーしました。コピーは、 Webアーカイブ および Ruby Buzzフォーラム にもあります。 。 - トカゲを請求する

186
hubbardr

このような:

people = []

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

参照: Rubyの隠しdo {} while()ループ

100
Blorgbeard

これはどう?

people = []

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

これがhubbardrの私のブログへのデッドリンクからの全文記事です。

RubyコアライブラリのTempfile#initializeのソースを読みながら、私は次のスニペットを見つけました:

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
11
jvoorhis

これは現在正しく機能します。

begin
    # statment
end until <condition>

しかし、beginステートメントは直感に反するので、将来は削除される可能性があります。参照: http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/Ruby/ruby-core/6745

Matzはこのようにすることを勧めました:

loop do
    # ...
    break if <condition>
end
8
Steely Wing

私が集めるものから、Matzはその構成を嫌います

begin
    <multiple_lines_of_code>
end while <cond>

なぜなら、それは意味論が違うからです

<single_line_of_code> while <cond>

つまり、最初の構文が条件をチェックする前に最初にコードを実行し、2番目の構文がコードを実行する前に最初に条件をテストします(ある場合)。私は、Matzがifステートメントの1行構成に一致するので、2番目の構成を保持することを好みます。

If文の場合でも、2番目の構文は好きではありません。他のすべての場合では、コンピュータはコードを左から右へ(例えば||や&&)上から下へ実行します。人間はコードを左から右に上から下に読みます。

代わりに次のような構成を提案します。

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

これらの提案が他の言語と一致するかどうかはわかりません。しかし、いずれにせよ私は言語の一貫性と同様に左から右への実行を維持することを好みます。

5
jds
a = 1
while true
  puts a
  a += 1
  break if a > 10
end
3
Paul Gillard

これは別のものです:

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