Ruby 中有“do ... while”循环吗?

Posted

技术标签:

【中文标题】Ruby 中有“do ... while”循环吗?【英文标题】:Is there a "do ... while" loop in Ruby? 【发布时间】:2010-09-13 07:14:12 【问题描述】:

我正在使用此代码让用户输入名称,而程序将它们存储在一个数组中,直到他们输入一个空字符串(他们必须在每个名称后按 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 中似乎不存在这种类型的循环。有人可以提出更好的方法吗?

【问题讨论】:

我觉得普通的while循环看起来更好看,也更容易阅读。 @Jeremy Ruten 您是否有兴趣将已接受的答案更改为沉思伟的答案loop do; ...; break if ...; end 【参考方案1】:

注意

begin <code> end while <condition> 被 Ruby 的作者 Matz 拒绝。相反,他建议使用Kernel#loop,例如

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

这是 2005 年 11 月 23 日的 an email exchange,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 的创建者 Yukihiro Matsumoto 对这个循环功能表示遗憾,并建议使用 Kernel#loop。

【讨论】:

正确。这个begin end while 方法似乎不对。感谢您为我提供说服团队其他成员所需的素材。 看起来 begin-end-while 循环实际上是在运行循环之前评估条件。这与常规的 while 循环之间的区别在于它保证至少运行一次。它只是足够接近......虽然会引起问题。 所以,据我了解,begin-end-while 是“遗憾的”,因为违反了修饰符的语义,即:在执行块之前对其进行检查,例如: puts k 如果 !k.nil?。这里的“if”是一个“修饰符”:在之前检查它,以确定是否执行“puts k”语句。while/until循环不是这种情况(当用作begin-end-block的修饰符时!),在第一次处决后进行评估。也许这引起了遗憾,但我们有比旧论坛帖子“更强大”的东西吗?就像在许多其他场合一样,官方弃用了它? 我花了很长时间才弄清楚为什么我使用这个 ruby​​ 'do-while' 循环不起作用。您应该使用“除非”来更接近地模仿 c 风格的 do-while,否则您可能最终会像我一样忘记反转条件:p @James 根据链接的邮件,他说他“后悔”添加它。有时人们会犯错误,即使他们是语言设计师。【参考方案2】:

我在阅读 Ruby 核心库中 Tempfile#initialize 的源代码时发现了以下 sn-p:

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

最初由 Jeremy Voorhis 撰写。内容已被复制到此处,因为它似乎已从原始站点中删除。副本也可以在Web Archive 和Ruby Buzz Forum 中找到。 -比尔蜥蜴

【讨论】:

由 Wayback Machine 提供:web.archive.org/web/20080206125158/http://archive.jvoorhis.com/… 这就是为什么在链接到外部网站时,我总是确保将相关信息复制到我的答案中。 为什么你希望 while 修饰符在 begin...end 的内容之前被计算?这就是 do..while 循环应该起作用的方式。为什么你会“很高兴再也见不到这个成语?”它出什么问题了?我很困惑。 begin...end 看起来像一个块,类似于 ...。没有错。 -1:沉思伟的回答解释说,begin .. end 有点不受欢迎。请改用loop do .. break if &lt;condition&gt;【参考方案3】:

像这样:

people = []

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

参考:Ruby's Hidden do while () Loop

【讨论】:

如果没有输入,这段代码不会给数组添加一个空字符串吗? 虽然这里不适用,但是 begin-end-while 构造的一个问题是,与所有其他 ruby​​ 构造不同,它不返回最后一个表达式的值:“begin 1 end while false" 返回 nil(不是 1,不是 false) 您可以使用until info.empty? 而不是while not info.empty? 实际上@AndrewR 这就是重点.. 在比较之前做一些事情.. do diplay("remaining =#count ) while(count > 0)... 我得到一个显示“剩余 = 0”.. 完美!【参考方案4】:

这个怎么样?

people = []

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

【讨论】:

但这不是“do ... while”循环。 :) 但在这种情况下它做同样的事情,除非我弄错了 @Blorgbeard,do..while 循环总是运行一次,然后评估它是否应该继续运行。传统的 while/until 循环可以运行 0 次。这不是一个巨大的差异,但它们是不同的。 @Scott,这是真的 - 我只是说这段代码等同于 OP,即使它不使用 do/while。虽然实际上,这段代码在条件中做了一半循环的“工作”,所以它也不是一个传统的 while 循环——如果条件不匹配,一些工作仍然完成。 刚试过这个。你可以有一个表单块 begin ... end until。酷!【参考方案5】:

这是来自 hubbardr 到我博客的死链接的全文文章。

我在阅读 Ruby 核心库中Tempfile#initialize 的源代码时发现了以下 sn-p:

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

【讨论】:

【参考方案6】:

现在可以正常工作了:

begin
    # statment
end until <condition>

但是,它可能会在将来被删除,因为begin 语句是违反直觉的。见:http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-core/6745

Matz(Ruby 的创建者)建议这样做:

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

【讨论】:

这看起来有点像 goto。代码混淆了你的意图。 对我来说看起来很棒,除了 while true 可以替换为 loop do @DavidWiniecki,确实,while true 可以替换为 loop do。但我在循环内用大量迭代测试了这两种结构,发现while true 至少比loop do 快2 倍。无法解释差异,但肯定存在。 (在测试 Advent of Code 2017 第 15 天时发现。)【参考方案9】:

这是另一个:

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

【讨论】:

我更喜欢这个,因为 unless 就在前面,我不会阅读一堆代码(可能比这里显示的更多)只是为了找到一个“悬空”unless在末尾。修饰符和条件在像这样“预先”放置时更容易使用,这是代码中的一般原则。 我有时希望我们的编码人员必须为每次额外的比较支付现金。而且它对我们的“外观”不如它对每天使用它一百万次的东西的外观那么重要。【参考方案10】:
ppl = []
while (input=gets.chomp)
 if !input.empty?
  ppl << input
 else
 p ppl; puts "Goodbye"; break
 end
end

【讨论】:

这看起来有点像 goto。代码混淆了你的意图,看起来很不红。 混淆* 不混淆。

以上是关于Ruby 中有“do ... while”循环吗?的主要内容,如果未能解决你的问题,请参考以下文章

Java 循环结构 - for, while 及 do...while

Java 循环结构 - for, while 及 do...while

Java 循环结构 - for, while 及 do...while

忘记在 do...while 循环中做

JAVA 循环结构 - for, while 及 do...while

VBA for Access - Do While 循环停止递减