Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ruby while syntax

Tags:

ruby

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 do with the "do" of a block?

like image 499
hellvinz Avatar asked Oct 26 '10 17:10

hellvinz


People also ask

What is while in Ruby?

Ruby while StatementExecutes code while conditional is true. A while loop's conditional is separated from code by the reserved word do, a newline, backslash \, or a semicolon ;.

How do you break a while loop in Ruby?

In Ruby, we use a break statement to break the execution of the loop in the program. It is mostly used in while loop, where value is printed till the condition, is true, then break statement terminates the loop. In examples, break statement used with if statement. By using break statement the execution will be stopped.

What does .times do in Ruby?

The times function in Ruby returns all the numbers from 0 to one less than the number itself. It iterates the given block, passing in increasing values from 0 up to the limit. If no block is given, an Enumerator is returned instead.


2 Answers

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.

like image 153
Jörg W Mittag Avatar answered Oct 29 '22 22:10

Jörg W Mittag


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

while (i = a.shift)
  puts i
end
like image 36
meagar Avatar answered Oct 30 '22 00:10

meagar