Multiple assignment in conditional

On Thu, Feb 2, 2012 at 3:47 PM, Robert K.
[email protected] wrote:

Probably because (a,b=foo) in all but the latest version always yields
an Array which is true hence does not make sense as a condition. You
could write if (true) as well.

ah, portability/compatibility. i guess we’re stucked then :slight_smile:
thanks Robert.
best regards -botp

On Thu, Feb 2, 2012 at 3:56 AM, botp [email protected] wrote:

puts “outcome 2”
SyntaxError: (irb):17: multiple assignment in conditional

but i do not know why it has to do that checking. too much work. and
could probably slow down ruby.

Probably because (a,b=foo) in all but the latest version always yields
an Array which is true hence does not make sense as a condition. You
could write if (true) as well.

08:45:50 ~$ allruby x.rb
CYGWIN_NT-5.1 padrklemme2 1.7.9(0.237/5/3) 2011-03-29 10:10 i686 Cygwin

ruby 1.8.7 (2008-08-11 patchlevel 72) [i386-cygwin]
[nil]
“true”

ruby 1.9.3p0 (2011-10-30) [i386-cygwin]
nil

jruby 1.6.3 (ruby-1.8.7-p330) (2011-07-07 965162f) (Java HotSpot™
Client VM 1.7.0_02) [Windows XP-x86-java]
[nil]
“true”

jruby 1.6.3 (ruby-1.9.2-p136) (2011-07-07 965162f) (Java HotSpot™
Client VM 1.7.0_02) [Windows XP-x86-java]
[nil]
“true”

08:47:11 ~$ cat -n x.rb
1
2 def foo;nil end
3
4 x = (a,b=foo)
5 p x
6
7 if (x = (a,b=foo))
8 p “true”
9 end
08:47:15 ~$

Kind regards

robert

On Thu, Feb 2, 2012 at 10:03 AM, botp [email protected] wrote:

ok, i’ll take it back, since,
if only the latest version makes sense, then wouldn’t that mean it’s
not really being used in the earlier versions, and therefore, it
is safe to implement it currently, ie remove the checking?

I think so. But that still does not make it a brilliant idiom in my
eyes. Granted, the example Gavin presented looks pretty reasonable.
However, I haven’t missed this feature in all those years so I am a
tad uncertain how common or uncommon that is.

Kind regards

robert

On Thu, Feb 2, 2012 at 4:57 PM, botp [email protected] wrote:

but i do not know why it has to do that checking. too much work. and
could probably slow down ruby.

Probably because (a,b=foo) in all but the latest version always yields
an Array which is true hence does not make sense as a condition. You
could write if (true) as well.

ah, portability/compatibility. i guess we’re stucked then :slight_smile:
thanks Robert.
best regards -botp

ok, i’ll take it back, since,
if only the latest version makes sense, then wouldn’t that mean it’s
not really being used in the earlier versions, and therefore, it
is safe to implement it currently, ie remove the checking?

kind regards -botp

On Thu, Feb 2, 2012 at 12:25 AM, Robert K.
[email protected] wrote:

I can’t test on 1.9.* right now but in 1.8.7 (a,b=nil) is always an
empty array => true. So if the method returns a single value nil you
probably do not get away without assigning that single value to a
variable and go from there. Or you test one of the array elements
only for nil.

Well spotted wrt 1.8.7. That behaviour is contrary to what I expect.
Assignment should always return the RHS, and nil means nil! I’m glad
it was changed.

On Thu, Feb 2, 2012 at 8:03 AM, Josh C. [email protected] wrote:

Could you just write what you expect the evaluation to be in each of these
cases?

a, b = 1, 1
a, b = 1, nil
a, b = nil, 1
a, b = nil, nil

I’d expect return values of [1, 1], [1, nil], [nil, 1], [nil, nil] which
are all true.

Yes, I expect what you expect.

Also,

a, b = nil # a and b both get the value nil

Now does this change when the RHS is an array instead of two values?
For example, would a, b = [1, 1] return a different value than a, b = [1, 1]?

No, because you wrote them exactly the same!

Assuming you meant to compare these two:

a, b = 1, 1
a, b = [1, 1]

then I expect them to behave exactly the same. I can see why you
expect it to set a to [1,1] and b to nil, but that’s just not the way
it is. If I’m not wrong, Ruby actually converts the first line above
into the second line behind the scenes. After all, that’s precisely
how you return multiple values from a method, or at least give the
appearance of doing so.

def two_values(x)
[x*2, x^2]
end

a, b = two_values(5)

From faint memory, to achieve the same thing in Perl you must wrap the
LHS in an array. Ruby’s equivalent would be

[a, b] = two_values(5) # This doesn’t work in Ruby, and it’s not
missed.

Your example seems particularly devious in that it only has one value on
the RHS of the assignment.

No it doesn’t. That’s not a value, per se, it’s a method call. The
method call returns either an array, which assigns the two variables
and activates the “if” branch, or nil, which activates the “else”
branch.

I didn’t approach this being devious or thinking it would be at all
controversial. I simply have a good intuition for assignment in Ruby.

This means that the second argument will be
assigned nil (because there is no value for it),

No. Try this in irb:

if _ = (a, b = [5,2,16,100])
puts a + b
end

And this.

a, b = nil
a, b = 1
a, b = 1, 2
a, b = [1, 2]
a, b = [1,2,3,4,5]

a, *b = nil
a, *b = 1
a, *b = 1, 2
a, *b = [1, 2]
a, *b = [1,2,3,4,5]

and see what these assignment operators return and what values get set
to a and b.

[…]

There are so many nuanced ruby edge cases one must know (and then must also
know the implementation of how it is being returned) to understand that
example that I think it has more overhead than it is worth.

There isn’t a single nuance [1]; it all follows from understanding
parallel assignment in Ruby. I didn’t go out of my way to learn that.
It must have been in one of the early Ruby books I read, or
something, and something I use all the time (especially multiple
returns from methods).

[1] in 1.9.3. The earlier versions have the nuance of

a, b = nil # Returns [nil] instead of nil.

That pre-1.9.3 behaviour is contrary to what I’ve always expected and
I’m glad to see it corrected. I can only surmise that the syntax
error “multiple assignment in conditional” is a hangover from those
days as a (fair) warning that it’s not going to do what you expect,
and no good can come of it. They’ve changed the behaviour, but left
the completely unnecessary syntax error.

Gavin

On Thu, Feb 2, 2012 at 6:35 PM, Robert K.
[email protected] wrote:

I think so. But that still does not make it a brilliant idiom in my
eyes. Granted, the example Gavin presented looks pretty reasonable.

given a conditional expression like,

if ( foo bar)

or

while ( foo bar )

shouldn’ t we be concerned only of the value of the parenthetical
expression? i think that is true for all languages, no?

i dunno. matz allows x,y = foo to return nil, but not if (x,y =
foo). i hope i can read matz brain :slight_smile:

However, I haven’t missed this feature in all those years so I am a
tad uncertain how common or uncommon that is.

w these both 1.9.3+ features, we can dry up some while routines, eg,

while (x,y,z=foo)
doit
end

cause otherwise, i’ll have to do it like this

x,y,z=foo
while (x)
doit
x,y,z=foo
end

Kind regards
robert

best regards -botp

On Thu, Feb 2, 2012 at 7:15 AM, Joel VanderWerf
[email protected] wrote:

You can trick ruby:

def foo; nil; end

if (_ = (a, b = foo)) […]

Done and done!

def week_and_day(date)
if not self.include? date
nil
elsif _ = (week, day = @t1.week_and_day(date))
[week, day]
elsif _= (week, day = @t2.week_and_day(date))
[week + @t1.number_of_weeks, day]
end
end

I guess I need to document a Ruby 1.9.3+ requirement :slight_smile: