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

Is there a “do … while” loop in Ruby?

我使用此代码让用户输入名称,而程序将其存储在数组中,直到输入空字符串(每个名称后必须按Enter键):

1
2
3
4
5
6
7
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循环:

1
2
3
4
5
6
people = []

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

在这段代码中,我不需要将信息分配给某个随机字符串。

不幸的是,这种类型的循环似乎不存在于Ruby中。有人能推荐一种更好的方法吗?


注意:

鲁比的作者Matz拒绝了begin end while 。相反,他建议使用Kernel#loop,例如

1
2
3
4
loop do
  # some code here
  break if <condition>
end

以下是2005年11月23日的邮件交换,Matz说:

1
2
3
4
5
6
7
8
9
10
11
12
|> 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 <wyn> end while <cond>

works differently from

  <wyn> while <cond>

Rosettacodewiki也有类似的故事:

During November 2005, Yukihiro Matsumoto, the creator of Ruby, regretted this loop feature and suggested using Kernel#loop.


I found the following snippet while reading the source for Tempfile#initialize in the Ruby core library:

1
2
3
4
5
6
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)

At first glance, I assumed the while modifier would be evaluated before the contents of begin...end, but that is not the case. Observe:

1
2
3
4
5
>> begin
?>   puts"do {} while ()"
>> end while false
do {} while ()
=> nil

As you would expect, the loop will continue to execute while the modifier is true.

1
2
3
4
5
6
7
8
9
10
>> n = 3
=> 3
>> begin
?>   puts n
>>   n -= 1
>> end while n > 0
3
2
1
=> nil

While I would be happy to never see this idiom again, begin...end is quite powerful. The following is a common idiom to memoize a one-liner method with no params:

1
2
3
def expensive
  @expensive ||= 2 + 2
end

Here is an ugly, but quick way to memoize something more complex:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def expensive
  @expensive ||=
    begin
      n = 99
      buf =""
      begin
        buf <<"#{n} bottles of beer on the wall
"

        # ...
        n -= 1
      end while n > 0
      buf <<"no more bottles of beer"
    end
end

最初是杰里米·沃希斯写的。内容已被复制到此处,因为它似乎是从原始网站上删除的。副本也可以在网络档案和RubyBuzz论坛上找到。-比尔蜥蜴


这样地:

1
2
3
4
5
6
people = []

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

参考:ruby的hidden do while()循环


这个怎么样?

1
2
3
4
5
people = []

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


这是从哈伯德的死链接到我的博客的全文文章。

在Ruby核心库中读取Tempfile#initialize的源代码时,我发现了以下代码片段:

1
2
3
4
5
6
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的内容之前进行评估,但事实并非如此。观察:

1
2
3
4
5
>> begin
?>   puts"do {} while ()"
>> end while false
do {} while ()
=> nil

正如您所期望的,当修饰符为真时,循环将继续执行。

1
2
3
4
5
6
7
8
9
10
>> n = 3
=> 3
>> begin
?>   puts n
>>   n -= 1
>> end while n > 0
3
2
1
=> nil

虽然我很高兴再也看不到这个成语,但begin...end是相当强大的。下面是一个常用的成语,用来记忆没有参数的一行方法:

1
2
3
def expensive
  @expensive ||= 2 + 2
end

下面是一个丑陋但快速的记忆更复杂事物的方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def expensive
  @expensive ||=
    begin
      n = 99
      buf =""
      begin
        buf <<"#{n} bottles of beer on the wall
"

        # ...
        n -= 1
      end while n > 0
      buf <<"no more bottles of beer"
    end
end

现在可以正常工作了:

1
2
3
begin
    # statment
end until <condition>

但是,由于begin的声明是违反直觉的,将来可能会删除它。参见:http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-core/6745

Matz建议这样做:

1
2
3
4
loop do
    # ...
    break if <condition>
end


据我所知,马茨不喜欢这个建筑

1
2
3
begin
    <multiple_lines_of_code>
end while <cond>

因为它的语义不同于

1
<single_line_of_code> while <cond>

在检查条件之前,第一个构造首先执行代码,第二个构造首先测试条件,然后再执行代码(如果有的话)。我认为Matz更喜欢保留第二个构造,因为它匹配if语句的一行构造。

我从不喜欢第二个结构,即使是if语句。在所有其他情况下,计算机从左到右(例如和&;&;)从上到下执行代码。人类从左到右阅读代码从上到下。

我建议采用以下结构:

1
2
3
4
5
6
7
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

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


1
2
3
4
5
6
a = 1
while true
  puts a
  a += 1
  break if a > 10
end


这是另一个:

1
2
3
4
5
6
7
8
people = []
1.times do
  info = gets.chomp
  unless info.empty?
    people += [Person.new(info)]
    redo
  end
end


1
2
3
4
5
6
7
8
ppl = []
while (input=gets.chomp)
 if !input.empty?
  ppl << input
 else
 p ppl; puts"Goodbye"; break
 end
end