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 <condition>
。【参考方案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