Ruby的while语法

9

有人知道为什么我可以写这个吗:

ruby-1.8.7-p302 > a = %w( a b c)
 => ["a", "b", "c"] 
ruby-1.8.7-p302 > while (i = a.shift) do; puts i ; end
a
b
c
 => nil 

这看起来像是将一个块传递给while循环,而不是:

while(i = a.shift) { puts i; }

是因为 while 语法中的 "do" 只是语法糖,与块中的 "do" 没有任何关系吗?

2个回答

13

这是因为while语法中的do只是一种语法糖,与块中的do没有关系吗?

大体上来说,是的。它不是语法糖,而是内置的语言结构,就像defclass一样,正如@meagar已经写过的。

它与块中的do无关,除了关键字很昂贵,因此重用关键字是有意义的(我所说的“昂贵”是指它们限制了程序员的表达能力)。

while循环中,有两种方法将块与条件分开:

  1. do关键字和
  2. 表达式分隔符。

而Ruby中有两种不同的表达式分隔符:

  1. 分号;以及
  2. 换行符。

因此,以下三种方式都是有效的:

while i = a.shift do puts i end # do

while i = a.shift; puts i end   # semicolon

while i = a.shift
  puts i end                    # newline
[显然,最后一个例子不应该这样写。你应该将 end 放在新的一行,并向左缩进以与 while 匹配。我只是想演示分离 while 循环各部分所需的最少代码。]
顺便说一下,把条件放在括号内是高度不惯用的。你的代码中还有很多多余的分号。而变量名 i 通常用于索引,而非元素。(我通常使用 el 来表示一般的元素,但我更喜欢更语义化的名称。)
手动迭代集合也是十分不惯用的。你的代码可以更好地编写为:
a.each(&method(:puts)).clear

这种写法不仅更易于理解(打印数组的所有元素并删除所有项),而且更容易编写(无法出错终止条件或弄错任何赋值语句)。它还更有效率:您的版本是Θ(n2),而这个版本是Θ(n)。

实际上,那并不是你应该写的方式,因为Kernel#puts已经实现了这种行为。所以,你真正应该写的是这个:

puts a
a.clear

或者可能是这个

a.tap(&method(:puts)).clear

[注意:最后一个选项并非完全等同。它会为一个空数组打印换行符,而其他所有选项则不会打印任何内容。]

简单。清晰。简洁。表达力强。快速。

与此相比:

while (i = a.shift) do; puts i ; end

我实际上需要运行多次才能完全理解它是做什么的。


1
Method#to_proc... 我想我有点爱上你了,Jorg。 - Matt Briggs

7

while不接受块,它是一种语言结构。 do是可选的:

while (i = a.shift)
  puts i
end

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接