Rubyに「do…while」ループはありますか?


452

このコードを使用して、ユーザーが名前を入力できるようにしますが、プログラムは空の文字列を入力するまで配列にそれらを格納します(各名前の後に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には存在しないようです。誰かがこれを行うより良い方法を提案できますか?


1
通常のwhileループは見栄えがよく、読みやすいと思います。
Magne

1
@ジェレミー・ルーテンは、あなたが受け入れられた答えをシウェイ・シェンの答えに変えることに興味がある可能性がありますloop do; ...; break if ...; endか?
David Winiecki、2014年

回答:


643

注意

begin <code> end while <condition>Rubyの作者マッツによって拒否されます。代わりに、彼はKernel#loop、例えば

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

ここだなメール交換マッツは述べて2005年11月23日では:

|> 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の使用を提案しました。


18
スポット。このbegin end while方法は正しくないようです。チームの他のメンバーを説得するのに必要な飼料をくれてありがとう。
Joshua Pinter、

2
begin-end-whileループが実際にループを実行する前に条件を評価しているようです。これと通常のwhileループの違いは、少なくとも1回は実行されることが保証されていることです。問題が発生する間...それを行うには十分に近いです。
user1992284 2013年

4
したがって、私がよく理解している限り、begin-end-whileは修飾子のセマンティクスに違反しているため、「後悔しています」。つまり、ブロックを実行する前にチェックされます。たとえば、kを挿入します。!k.nil ?. ここで 'if'は '修飾子'です: 'puts k'ステートメントを実行するかどうかを判断するためにBEFOREがチェックされます。while / untilループの場合とは異なります(begin-end-blockの修飾子として使用される場合) !)、最初の実行後に評価されます。おそらくこれは後悔の原因となったかもしれませんが、他の多くの場合に使用されているような、古いフォーラム投稿よりも「強力」なものですか?
AgostinoX、2015年

2
このルビーの「do-while」ループの使用が機能しない理由を理解するのに、恥ずかしいほどの時間がかかりました。'unless'を使用してcスタイルのdo-whileをより厳密に模倣する必要があります。そうしないと、私のようになって条件を反転するのを忘れることがあります:p
Connor Clark

1
@Jamesリンクされたメールによると、彼はそれを「後悔」していると彼は言った。言語デザイナーであっても、人は間違いを犯すことがあります。
Xiong Chiamiov

188

Tempfile#initializeRubyコアライブラリのソースを読んでいるときに、次のスニペットを見つけました。

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

元々はJeremy Voorhisによって書かれました。元のサイトから削除されたようですので、コンテンツはここにコピーされました。コピーは、Web ArchiveRuby Buzz Forumにもあります。-トカゲの請求書



56
これが、外部サイトにリンクするときに、関連情報を必ずここに私の回答にコピーする理由です。
davr

10
begin ... endの内容の前にwhile修飾子が評価されることを期待するのはなぜですか?これが、.. whileループが機能するはずの方法です。そして、なぜあなたは「このイディオムを二度と見ないことを嬉しく思いますか?」どうしたの?よくわかりません。
bergie3000 2013

2
begin ... endも同様に{...}のようにブロックのように見えます。何も問題はありません。
Victor Pudeyev 2013年

1
-1:Siwei Shenの回答は、それbegin .. endがやや眉をひそめていることを説明しています。loop do .. break if <condition>代わりに使用してください。
ケビン

102

このような:

people = []

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

参照:Rubyの非表示の実行{} while()ループ


1
へー、それに負けた。くそー。
Blorgbeardは

入力がない場合、このコードは配列に空の文字列を追加しませんか?
AndrewR 2008

1
ここでは適用されませんが、begin-end-while構文の問題の1つは、他のすべてのルビ構文とは異なり、最後の式の値を返さないことです。 "begin 1 end while false"はnilを返します(1ではありません) falseではありません)
tokland

9
until info.empty?代わりに使用できますwhile not info.empty?
Andrew Grimm

実際には@AndrewRがポイントのようなものです..比較の前に物事を行う.. do diplay( "remaining =#{count})while(count> 0)..." remaining = 0 "..と表示されます。パーフェクト!
baash05

45

これはどう?

people = []

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

4
とてもエレガントです。
Blorgbeardは、

23
しかし、これは "do ... while"ループではありません。:)
アレクサンダープロコフィエフ

4
しかし、私が間違っていない限り、それはこの場合でも同じことを行います
Blorgbeardは'09 / 09/30

18
@ Blorgbeard、do..whileループは常に1回実行され、次に実行を継続する必要があるかどうかを評価します。従来のwhile / untilループは0回実行できます。大きな違いではありませんが、違いはあります。
Scott Swezey、2010年

5
@スコット、それは本当です-do / whileを使用していなくても、このコードがOPと同等であることを意味しました。実際には、このコードは条件内でループの「作業」の半分を実行するため、従来のwhileループでもありません。条件が一致しない場合でも、いくつかの作業が行われます。
Blorgbeardは2010

11

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

Tempfile#initializeRubyコアライブラリのソースを読んでいるときに、次のスニペットを見つけました。

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


6

私が集めたものから、マッツは構造が好きではありません

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

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


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

3
これは後藤のように見えます。コードはあなたの意図を難読化します。
Gerhard

while trueに置き換えることができることを除いて、私には見栄えがしloop doます。
David Winiecki 2014年

@DavidWinieckiは実際、while trueに置き換えることができますloop do。しかし、私は、ループ内の反復の多い両構築をテストし、その発見されたwhile true少なくとも2倍であるより速くよりloop do。違いは説明できませんが、間違いなくあります。(Advent of Code 2017のテスト中に発見されました。15日目)
Jochem Schulenklopper

2

ここに別のものがあります:

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

unlessはこれが真っ先unlessにあるので、最後に「ぶら下がっている」を見つけるためだけにコードの束(ここに示されている以上のものになる可能性があります)を読まないので、これを好みます。修飾子と条件がこのように「前に」あると、修飾子と条件が使いやすくなるというのは、コードの一般原則です。
マイケル・デュラント

コーダーが余分な比較ごとに現金を支払わなければならないことを願っています。そして、それが私たちにとってどのように「見える」かは、それを1日に100万回使用するものにとってどのように見えるかほど重要ではありませんでした。
baash05

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

2
これは後藤に少し似ています。コードはあなたの意図を難読化し、非常に不器用に見えます。
Gerhard

obfuscate *は難読化しません。
qedk
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.