Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ruby parallel assignment

Tags:

ruby

I want to test the return value of a parallel assignment, and I write puts (x, y = 1, 2), but it doesn't work, and print the error message:

SyntaxError: (irb):74: syntax error, unexpected ',', expecting ')'
puts (x,y =1,2)
    ^
(irb):74: syntax error, unexpected ')', expecting end-of-input

Is there anything wrong?

like image 935
jsvisa Avatar asked Oct 16 '13 12:10

jsvisa


3 Answers

You have two problems.

  1. The space between puts and the ( prevents the parenthesized list from being interpreted as an argument list. Once you put a space after a method name, any argument list has to be outside of parentheses, and any parenthesized parts must be a legal expressions. In Ruby, (x,y,z) is not a legal single expression, so you get the above error.

    If you remove the space, you get this:

    > puts(x, y = 1, 2)    
    NameError: undefined local variable or method `x' for main:Object
    

    That's because of your second problem:

  2. The statement puts(x,y = 1,2) is parsed as puts(x, y=1, 2); that is, Ruby interprets it as passing three arguments to puts: x is the first, y=1 is the second, and 2 is the third. Since the x is not on the left side of an assignment and hasn't been defined yet, you get the above error.

    Use an extra set of parentheses to group the entire assignment together as one argument:

    > puts((x,y=1,2))    
    1    
    2    
    

But note that this is passing a single list that contains two items. It doesn't make a difference with puts, but it will for methods that distinguish between lists and individual parameters:

> def foo(a,b) puts "a=#{a},b=#{b}" end
> foo((x,y=1,2))
ArgumentError: wrong number of arguments (1 for 2)

So in that case you need one more piece of punctuation - the splat operator:

> foo(*(x,y=1,2))   
a=1, b=2

Interesting, but of little practical relevance, is the fact that once you've doubled the parentheses, you can put the space back if you want:

> puts ((x, y = 1, 2))    
1    
2    

But again, that turns them from argument-wrappers into just an extra expression-wrapper; you could put any number of parentheses around that without changing anything. That means that in the foo case, the splat operator has to go outside both sets of parentheses:

> foo (*(x,y=1,2))
SyntaxError: (irb):24: syntax error, unexpected tSTAR
> foo *((x,y=1,2))
a=1, b=2

It's generally considered bad style in Ruby to use the parenthesisless form when the first argument itself includes parentheses, however. Depending on your Ruby version you may get a warning about such statements, but even if you don't, it's better to use the fully-parenthesized version (with no space after the method name).

like image 178
Mark Reed Avatar answered Oct 08 '22 00:10

Mark Reed


This should work

puts( (x, y = 1, 2) )
like image 5
crackedmind Avatar answered Oct 08 '22 00:10

crackedmind


puts((x, y = 1, 2))
# >> 1
# >> 2

One set of parentheses for method call, another for decorating assignment as an explicit expression.

like image 2
Sergio Tulentsev Avatar answered Oct 08 '22 00:10

Sergio Tulentsev