1

To populate an array I could write [*2..10], which gives [2, 3, 4, 5, 6, 7, 8, 9, 10].

How does this work?

the Tin Man
  • 158,662
  • 42
  • 215
  • 303
  • 1
    It's defined in Ruby syntax. It works because they defined it to work. – lurker Sep 01 '13 at 18:50
  • Please look at http://stackoverflow.com/questions/918449/what-does-the-unary-operator-do-in-this-ruby-code, and the linked answers on the righthand side of the question. – the Tin Man Sep 01 '13 at 19:05
  • 1
    Also, the "splat" tests are a good way to see what it's supposed to do: https://github.com/rubyspec/rubyspec/blob/master/language/splat_spec.rb – the Tin Man Sep 01 '13 at 19:16

3 Answers3

9

The expression 2..10 uses the .. operator to create a Range object.

In general, * flattens arrays, in order to convert a single object into a list of individual arguments to a method. When applied to a non-array object, it will attempt to convert the object into an Array first by calling to_a on it. So applying * to the Range first calls to_a on it, which returns an array of the values that lie in the range. That array is then flattened into a list of values by the *.

Finally, putting that list of values inside square brackets [...] generates a new Array. It's the same result as just calling to_a on the Range, but it's gone through a couple extra steps along the way (flattening by * and unflattening by []).

Note that on the right hand side of an assignment, you can do without the square brackets, whether literal (x=1,2,3 yields the same result as x=[1,2,3]) or splatted (x=*2..10 yields the same result as x=[*2..10]).

Mark Reed
  • 91,912
  • 16
  • 138
  • 175
2

The splat operator * unpacks the receiver's elements in place if the receiver is an array. If the receiver is not an array, implicit class conversion is attempted in advance, using the method to_a.

In your case, range 2..10 is not an array, so to_a is attempted, which succeeds because Range#to_a is defined, and gives [2, 3, 4, 5, 6, 7, 8, 9, 10]. The splat operator unpacks the elements of that array within the outer [], resulting in [2, 3, 4, 5, 6, 7, 8, 9, 10].

sawa
  • 165,429
  • 45
  • 277
  • 381
0

Or (1..5).to_a.

Also keep in mind that x..y includes y in the range, but x...y includes up until the y, i.e:

(1...5).to_a

yields [1,2,3,4].

the Tin Man
  • 158,662
  • 42
  • 215
  • 303
Beartech
  • 6,173
  • 1
  • 18
  • 41
  • 1
    It's not a good idea to use the `...` operator. Too many people don't know what the triple-dot means, which can lead to errors during debugging and maintenance. Rather than say "includes up to the 'y'" it's probably better to say it "excludes 'y'". – the Tin Man Sep 01 '13 at 19:09
  • 1
    If you are just creating a range then yes, `..` is preferable. But you may have a more complex construct where you want to count up to, but not include, some variable. Then it might be better to use `1...x` rather than `1..(x-1)`. I'm assuming that's why they put `...` in the language. – Beartech Sep 01 '13 at 21:30
  • This does not provide an answer to the question. To critique or request clarification from an author, leave a comment below their post. – sawa Sep 01 '13 at 23:57
  • More likely Matz put it in the language because it was in Perl. Ruby has a lot of Perlisms. – the Tin Man Sep 02 '13 at 01:05
  • 1
    @theTinMan "Too many people don't know what the triple-dot means" is not a good reason to avoid using such feature. If there are such people, it is their problem of not knowing Ruby syntax. Using it should not be discouraged because of such people. (But at the same time, I know that Matz recently expresses regret that he put too much perlism in Ruby. But particularly for `...`, it is useful.) – sawa Sep 02 '13 at 09:53
  • When a particular construct has a higher likelihood of incurring errors during the maintenance cycles of software, that construct has to be weighed very carefully before being used. When it can easily be replaced by decrementing the final value by one, becoming more clear and obvious what is happening, and reducing the chance of errors, then the construct should be avoided. `...` has been recognized as that sort of problem-child many times in Perl. Arguing that it has its place in Ruby simply because it exists makes little sense to me. `1..5` is much more obvious than `1...6`. – the Tin Man Sep 02 '13 at 18:28