474

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

4

10 回答 10

681

注意

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 的创建者 Yukihiro Matsumoto 对这个循环功能表示遗憾,并建议使用 Kernel#loop。

于 2012-05-23T05:28:38.377 回答
188

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 非常强大。以下是记忆没有参数的单行方法的常用习惯用法:

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 论坛中找到。- 蜥蜴比尔

于 2008-09-25T23:19:05.457 回答
103

像这样:

people = []

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

参考:Ruby 的 Hidden do {} while () 循环

于 2008-09-25T23:20:30.147 回答
44

这个怎么样?

people = []

until (info = gets.chomp).empty?
  people += [Person.new(info)]
end
于 2008-09-25T23:24:03.133 回答
12

这是从 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它非常强大。以下是记忆没有参数的单行方法的常用习惯用法:

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
于 2010-07-22T15:27:46.767 回答
11

这现在可以正常工作:

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
于 2013-02-17T06:26:31.810 回答
6

据我所知,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

我不知道这些建议是否会与其他语言一起解析。但无论如何,我更喜欢保持从左到右的执行以及语言的一致性。

于 2013-08-28T18:23:59.267 回答
3
a = 1
while true
  puts a
  a += 1
  break if a > 10
end
于 2010-08-04T23:51:43.687 回答
2

这是另一个:

people = []
1.times do
  info = gets.chomp
  unless info.empty? 
    people += [Person.new(info)]
    redo
  end
end
于 2011-11-10T12:38:58.283 回答
-3
ppl = []
while (input=gets.chomp)
 if !input.empty?
  ppl << input
 else
 p ppl; puts "Goodbye"; break
 end
end
于 2010-01-11T18:47:58.170 回答