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拒绝了
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 ()
=> nilAs 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
=> nilWhile 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
endHere 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核心库中读取
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) |
。
乍一看,我假设
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 |
。
虽然我很高兴再也看不到这个成语,但
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> |
号
但是,由于
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 |