Splat array with 1 value in Ruby 1.9 vs Ruby 1.8

In porting some automation code from 1.8.7 to 1.9.1, I find that in ruby
1.8 the following generated code (real code is unpacking binary strings
and assigning variables, but I simplify for clarity, although the result
may look a bit silly) worked:

var1 = *[ val1 ] # var1 =
val1
var1, var2, varn = *[ val1, val2, valn ] # ,

(the splat is not necessary in the second case, but this allowed the
code to be generated in the same way, regardless of array size).

With Ruby 1.9.1-p243, instead, this happens:

var1 = *[ val1 ] # var1 = [
val1 ] (!)
var1, var2, varn = *[ val1, val2, valn ] # var1 = val1

So in the first case, var1 became an array (instead than the expected
integer).
Of course, this can be fixed by generating code that checks the array
size (and doing a shift if 1, etc). But I am curious on this behavior of
splat when the array contains one element (I read about the
differences/new features of splat in 1.9, but they seem to have nothing
to do with this). Does anyone know if it is a bug?

Thanks

Raul P.

[ Sorry for the horrible indentation. I repeat the text renouncing to
align the expressions ]

In porting some automation code from 1.8.7 to 1.9.1, I find that in ruby
1.8 the following worked (real code is unpacking binary strings and
assigning variables, but I simplify for clarity):

var1 = *[ val1 ] # => var1 = val1

var1, var2, varn = *[ val1, val2, valn ]

(I know that the splat is not necessary in the second case, but this
allowed the code to be generated in the same way, regardless of array
size).

With Ruby 1.9.1, instead, this happens:

var1 = *[ val1 ] # => var1 = [ val1 ] (!)

var1, var2, varn = *[ val1, val2, valn ]

Of course, this can be handled by generating code that checks the array
size. But I am curious on this behavior of splat when the array contains
one element (I read about the features of splat in 1.9, but they seem to
have nothing to do with this). Does anyone know if it is a bug or
intentional?

Thanks

On Tue, Dec 1, 2009 at 12:53 AM, Raul P. [email protected]
wrote:

Of course, this can be handled by generating code that checks the array
size. But I am curious on this behavior of splat when the array contains
one element (I read about the features of splat in 1.9, but they seem to
have nothing to do with this). Does anyone know if it is a bug or
intentional?

Not sure exactly what you mean by generating the code, but could you use

var1 = val1
and
var1, var2, varn = val1, val2, valn


Rick DeNatale

Blog: http://talklikeaduck.denhaven2.com/
Twitter: http://twitter.com/RickDeNatale
WWR: http://www.workingwithrails.com/person/9021-rick-denatale
LinkedIn: http://www.linkedin.com/in/rickdenatale

2009/12/1 Raul P. [email protected]:

a = *[ 3 ] # => a = [ 3 ] with Ruby 1.9.1

I think that this is a bug in Ruby 1.9.

Regardless whether it is or is not a bug (I would not be so sure of
that), there is a way to fix it and handle it uniformly across Ruby
versions:

17:31:18 test$ allruby -e ‘a = [123];b = *[456];c, = [789]; p a,b,c’
CYGWIN_NT-5.1 padrklemme1 1.5.25(0.156/4/2) 2008-06-12 19:34 i686 Cygwin

ruby 1.8.7 (2008-08-11 patchlevel 72) [i386-cygwin]
[123]
456
789

ruby 1.9.1p129 (2009-05-12 revision 23412) [i386-cygwin]
[123]
[456]
789

jruby 1.4.0 (ruby 1.8.7 patchlevel 174) (2009-11-02 69fbfa3) (Java
HotSpot™ Client VM 1.6.0_17) [x86-java]
[123]
456
789

Look at variable “c”.

Kind regards

robert

Rick Denatale wrote:

On Tue, Dec 1, 2009 at 12:53 AM, Raul P. [email protected]
wrote:

Of course, this can be handled by generating code that checks the array
size. But I am curious on this behavior of splat when the array contains
one element (I read about the features of splat in 1.9, but they seem to
have nothing to do with this). Does anyone know if it is a bug or
intentional?

Not sure exactly what you mean by generating the code, but could you use

var1 = val1
and
var1, var2, varn = val1, val2, valn


Rick DeNatale

Blog: http://talklikeaduck.denhaven2.com/
Twitter: http://twitter.com/RickDeNatale

As I said at the beginning of the post, the code is unpacking binary
strings, which generates an array (else of course I would have not added
the array, just for the fun of having to remove it). For example:

x, y = “\x23\x00\x61”.unpack(‘Cn’) # => [35, 97]; so: x = 35; y = 97

Now, when there is just one variable to extract, we get (as expected):

z=“\x00\x61”.unpack(‘n’) # z = [97]

To get the scalar values in both cases, in 1.8.7 a splat operator in
front of the right value expression (placed by the code generator) does
the job; in 1.9.1, it does not. The solution is simple, but I was not
asking for “a solution”.

In other words, I was just pointing out this difference of behavior:

a = *[ 3 ] # => a =3 with Ruby 1.8.7

a = *[ 3 ] # => a = [ 3 ] with Ruby 1.9.1

I think that this is a bug in Ruby 1.9.

Raul P.

x =

Robert K. wrote:

2009/12/1 Raul P. [email protected]:

�a = *[ 3 ] � # => a = [ 3 ] � �with Ruby 1.9.1

I think that this is a bug in Ruby 1.9.

Regardless whether it is or is not a bug (I would not be so sure of
that), there is a way to fix it and handle it uniformly across Ruby
versions:

17:31:18 test$ allruby -e ‘a = [123];b = *[456];c, = [789]; p a,b,c’
CYGWIN_NT-5.1 padrklemme1 1.5.25(0.156/4/2) 2008-06-12 19:34 i686 Cygwin

ruby 1.8.7 (2008-08-11 patchlevel 72) [i386-cygwin]
[123]
456
789

ruby 1.9.1p129 (2009-05-12 revision 23412) [i386-cygwin]
[123]
[456]
789

jruby 1.4.0 (ruby 1.8.7 patchlevel 174) (2009-11-02 69fbfa3) (Java
HotSpot™ Client VM 1.6.0_17) [x86-java]
[123]
456
789

Look at variable “c”.

Kind regards

robert

Indeed, this solves with grace the problem in the automation (without
the easy but disturbing question on “how many elements the array has”):

a,b, = [ 3, 4 ] # => a = 3; b = 4
a, = [ 3 ] # => a = 3

On the other matter ( a = *[ 3 ] ), I still think that it is a bug in
1.9 (else I don’t understand fully what a splat operator is, and I’d
like to know more).

But thanks a lot, Robert

Raul P.

Robert K. wrote:

On 12/01/2009 05:56 PM, Raul P. wrote:

[456]
Kind regards
1.9 (else I don’t understand fully what a splat operator is, and I’d
like to know more).

I know there have been some things changed with regard to how the splat
operator works, including more complex patterns:

robert@fussel:~$ ruby1.9 -e ‘def f(a,*b,c)p a,b,c end;f(1, 2, 3, 4)’
1
[2, 3]
4

This did not work in 1.8:

The changes may make it necessary that 1.9 behaves the way you observed.
That’s why I said I am not sure whether it is actually a bug.

Hmm… the difference is:
a) in 1.8 the splat operator could only be applied to the last
left-value
b) in 1.9 the splat operator can appear at any position in the list of
left-values

But the concept of the operation has remained identical: all extra
rvalues are placed into an array

Similarly, 1.9 adds more flexibility when the splat is done on an rvalue
(multiple splats). But the concept is still the same (in this case, as
Matz/Flanagan’s book says “the array elements replace the array in the
original rvalue”). That’s the reason that I think the behavior of
a = *[ 3 ] # => a = [ 3 ]
is a bug; else, it would go against the definition just given (just
because the array has 1 element). Just my opinion, of course.


In any case, thanks again for the elegant solution to my code generation

Raul P.

On 12/01/2009 05:56 PM, Raul P. wrote:

[456]
Kind regards
1.9 (else I don’t understand fully what a splat operator is, and I’d
like to know more).

I know there have been some things changed with regard to how the splat
operator works, including more complex patterns:

robert@fussel:~$ ruby1.9 -e ‘def f(a,*b,c)p a,b,c end;f(1, 2, 3, 4)’
1
[2, 3]
4

This did not work in 1.8:

robert@fussel:~$ ruby1.8 -e ‘def f(a,*b,c)p a,b,c end;f(1, 2, 3, 4)’
-e:1: syntax error, unexpected tIDENTIFIER, expecting tAMPER or ‘&’
def f(a,*b,c)p a,b,c end;f(1, 2, 3, 4)
^
robert@fussel:~$

The changes may make it necessary that 1.9 behaves the way you observed.
That’s why I said I am not sure whether it is actually a bug.

But thanks a lot, Robert

You’re welcome!

Kind regards

robert

2009/12/1 Raul P. [email protected]:

(multiple splats). But the concept is still the same (in this case, as
Matz/Flanagan’s book says “the array elements replace the array in the
original rvalue”). That’s the reason that I think the behavior of
a = *[ 3 ] # => a = [ 3 ]
is a bug; else, it would go against the definition just given (just
because the array has 1 element). Just my opinion, of course.

If you believe you have found a bug, please file it so it can be taken
care of.

In any case, thanks again for the elegant solution to my code generation

You’re welcome!

Kind regards

robert

Well, so what’s the solution to get the single element in the array or
the
whole array when multiple elements ?

for exemple:

def my_method_handling_single_or_multiple

“str”.scan(/…/)
end

how to make this in a beautiful way?
I would like to have only the element if there is only one

of course, there is:
arr = “str”.scan(/…/)
arr.length == 1 ? arr[0] : arr

but that create a useless new variable and doesn’t look good at all.

B.D.

2009/12/2 Raul P. [email protected]

Benoit D. wrote:

Well, so what’s the solution to get the single element in the array or
the
whole array when multiple elements ?

how to make this in a beautiful way?
I would like to have only the element if there is only one

of course, there is:
arr = “str”.scan(/…/)
arr.length == 1 ? arr[0] : arr

but that create a useless new variable and doesn’t look good at all.

Benoit, str.scan(pattern) always returns an array of matches; you then
need to process the resulting array (for example, iterating on it,
extracting the elements of the array). You seem to want a different
result depending if the array size is 1 or not (that is by the way the
opposite of what this post was about, on a different problem).

I find that suspect; think about it: from that moment on, the rest of
the program will have to deal with a result that is of different type
depending on the fact that there was only 1 value or not in the array?
this is odd, and it is usually the contrary of what you want.
Anyhow, if you want to do such a thing, yes, test for array size and
extract the value (with shift or [0], etc). And at that point, you will
happily have a scalar value or an array depending on the number of
elements in the original array.


For clarity:
the original problem discussed in the thread was this: the splat
operator does not do what it used to when there is only 1 element in the
array. Robert K. has indicated above a solution to this (using the
‘comma’ notation at the end of left values). I posted a bug against
Ruby 1.9.
This problem posted by Benoit above has nothing to do with this.

Robert K. wrote:

2009/12/1 Raul P. [email protected]:

(multiple splats). But the concept is still the same (in this case, as
Matz/Flanagan’s book says “the array elements replace the array in the
original rvalue”). That’s the reason that I think the behavior of
� � �a = *[ 3 ] � �# => a = [ 3 ]
is a bug; else, it would go against the definition just given (just
because the array has 1 element). Just my opinion, of course.

If you believe you have found a bug, please file it so it can be taken
care of.
http://redmine.ruby-lang.org/

In any case, thanks again for the elegant solution to my code generation

You’re welcome!

Kind regards

robert

Thanks for the suggestion; I just reported (what I believe to be) the
bug, with the quote from Matz’s book

Raul P.

Humm, well, it has a very-not-easy-to-see link, but it is.

I imagined at a moment using the splat operator, while it can of course
not
work without any variable.

I use code above to allow dynamic parameters based on the name of the
variable who’s result of that method is assigned to that variable.
(for exemple :
x = var
allow to pass a parameter “x” to the method var() )

So I’m scanning the code (probably not a good idea but …) and then I
want this method to return an array if I do:
x, y = var
or a single object like above.

Ok, I’ll just keep the “.length” code … but I asked if there was a
feature
on Array acting like a splat operator.

class Array
def splat
length == 1 ? shift : self
end
end

(then in 1.8, I imagined to do: “*result” at the last line of the
method.
irb(main):001:0> def m
irb(main):002:1> arr = [2]
irb(main):003:1> *arr
irb(main):004:1> end
SyntaxError: compile error
(irb):3: syntax error, unexpected ‘\n’, expecting ‘=’
from (irb):4
irb(main):005:0> def m
irb(main):006:1> arr = [2]
irb(main):007:1> return *arr
irb(main):008:1> end
=> nil
irb(main):009:0> m
=> 2
For once, return is needed it seems …)

2009/12/19 Raul P. [email protected]

Please do not top post.

On 20.12.2009 00:28, Benoit D. wrote:

So I’m scanning the code (probably not a good idea but …) and then I
want this method to return an array if I do:
x, y = var
or a single object like above.

I believe you are overlooking something: the very moment that you do “x
= meth(…)” you are expecting a single value or you are expecting
an Array with any number of elements. If you do “a,b,c = meth(…)” you
are expecting multiple values, typically three. The expectation is
expressed by the code that follows this line, i.e. if you do

x = meth(…)
x.each {|m| … }

your code expects an Array with any number of entries. If you do

x = meth(…)
puts Integer(x)

your code expects a single value. If you have these two different use
cases of your method, it makes every sense in the world to have two
different methods. It’s easier for the implementation and it is more
efficient as well because when you expect a single value you can use
String#[] or a regexp match with =~ instead of scanning the whole
string.

Now if you argue that you want to use the same regexp in both cases,
that’s not too difficult: both methods can share the regexp.

RX = /…/

def m1(…)
s[RX]
end

def mn(…)
s.scan RX
end

In fact, if the implementation is just a single line both methods might
actually be superfluous and you could use the regular expression
directly.

Your wish may be motivated by Perl experience where a method can
actually find out what the caller expects but Ruby is different and I
believe it does not make sense to try to force perlisms into a Ruby
program.

Btw, there’s also a different way to deal with this:

def m(…)
s.scan …
end

a, = m(…)
a,b,c = m(…)

By doing that every variable receives a single value and you do can
always return an Array.

irb(main):001:0> def demo(items) Array.new(items) {|i| i} end
=> nil
irb(main):002:0> demo(5)
=> [0, 1, 2, 3, 4]

irb(main):003:0> a, = demo(5); a
=> 0
irb(main):004:0> a,b,c = demo(5)
=> [0, 1, 2, 3, 4]
irb(main):005:0> a
=> 0
irb(main):006:0> b
=> 1
irb(main):007:0> c
=> 2

irb(main):012:0> a,b,c = demo(2)
=> [0, 1]
irb(main):013:0> a
=> 0
irb(main):014:0> b
=> 1
irb(main):015:0> c
=> nil

Kind regards

robert

2009/12/20 Robert K. [email protected]

work without any variable.
or a single object like above.

s.scan RX

irb(main):001:0> def demo(items) Array.new(items) {|i| i} end
irb(main):006:0> b
irb(main):015:0> c

Thanks for your answer, I think in fact creating 2 methods is the best way.
And to keep it very simple I can do:

def vars
… # => Array
end
def var
vars.shift
end

Regards,
Benoit

2009/12/20 Benoit D. [email protected]:

Thanks for your answer, I think in fact creating 2 methods is the best way.
And to keep it very simple I can do:

def vars
… # => Array
end
def var
vars.shift
end

That’s a good approach. By that you can still change the
implementation of #var if it proves to be too slow (e.g. if you are
often scanning large strings and the single match is at the
beginning).

Kind regards

robert