开发者

Ruby while syntax

Does anybody why I can write this:

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 

Which looks like passing a block to while. And not:

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

Is it because the "do" of the while syntax is just syntaxic sugar and as nothing to d开发者_开发知识库o with the "do" of a block?


Is it because the do of the while syntax is just syntaxic sugar and as nothing to do with the do of a block?

More or less, yes. It's not syntactic sugar, it's simply a built-in language construct, like def or class, as @meagar already wrote.

It has nothing to do with the do of a block, except that keywords are expensive and so reusing keywords makes sense. (By "expensive" I mean that they limit the programmer in his expressiveness.)

In a while loop, there are two ways to separate the block from the condition:

  1. the do keyword and
  2. an expression separator.

There are, in turn, two different expression separators in Ruby:

  1. the semicolon ; and
  2. a newline

So, all three of the following are valid:

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

[Obviously, that last one wouldn't be written that way, you would put the end on a new line, dedented to match the while. I just wanted to demonstrate what is the minimum needed to separate the parts of the while loop.]

By the way: it is highly un-idiomatic to put the condition in parentheses. There's also a lot of superfluous semicolons in your code. And the variable name i is usually reserved for an index, not an element. (I normally use el for generic elements, but I much prefer more semantic names.)

It is also highly un-idiomatic to iterate a collection manually. Your code would be much better written as

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

Not only is it much easier to understand what this does (print all elements of the array and delete all items from it), it is also much easier to write (there is no way to get the termination condition wrong, or screw up any assignments). It also happens to be more efficient: your version is Θ(n2), this one is Θ(n).

And actually, that's not really how you would write it, either, because Kernel#puts already implements that behavior, anyway. So, what you would really write is this

puts a
a.clear

or maybe this

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

[Note: this very last one is not 100% equivalent. It prints a newline for an empty array, all the other ones print nothing.]

Simple. Clear. Concise. Expressive. Fast.

Compare that to:

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

I actually had to run that multiple times to be 100% clear what it does.


while doesn't take a block, it's a language construct. The do is optional:

while (i = a.shift)
  puts i
end
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜