What does the syntax [*a..b] mean in Ruby?
Asked Answered
S

3

8

NOTE: mischa's splat on GitHub has lots of cool interactive examples of * in action.

By googling, I found that one way to iterate over a range of numbers in Ruby (your classic C-style for loop)

for (i = first; i <= last; i++) {
  whatever(i);
}

is to do something like this

[*first..last].each do |i|
  whatever i
end

But what exactly is going on with that [*first..last] syntax? I played around with irb and I see this:

ruby-1.9.2-p180 :001 > 0..5
 => 0..5 
ruby-1.9.2-p180 :002 > [0..5]
 => [0..5] 
ruby-1.9.2-p180 :003 > [*0..5]
 => [0, 1, 2, 3, 4, 5] 
ruby-1.9.2-p180 :004 > *0..5
SyntaxError: (irb):4: syntax error, unexpected tDOT2, expecting tCOLON2 or '[' or '.'
*0..5
    ^

Everything I've read online discusses the unary asterisk as being useful for expanding and collapsing arguments passed to a method, useful for variable length argument lists

def foo(*bar)
  bar 
end

foo 'tater' # => ["tater"]
foo 'tater', 'tot' # => ["tater", "tot"]

and I get that, but I don't see how it applies to the expansion being done in my block example above.

To be clear, I know that The Ruby Way is to iterate over an array or collection, not to use the array length and iterate with an integer index. However, in this example, I really am dealing with a list of integers. :)

Serenata answered 12/5, 2011 at 17:12 Comment(3)
My whole approach may be wrong. I should probably just be doing (first..last).each ..., but I'd still like to know what's going on there. :)Serenata
Try also: x=*0..5;p x and a=[1,2,3];b=[4,5,6];c=[*a,*b];p c (Ruby 1.9+)Amenra
You can also iterate using first.upto(last){ |i| ... } or first.step(last){ |i| ... } (where the latter method allows you to step by increments other than 1)Amenra
H
13
[*1..10]

is the same thing as

(1..10).to_a # call the "to array" method

Instances of the Array class you have created implement Enumerable so your loop works. On classes that define a to_a method, you can use the splat operator syntax with brackets. Splat does a lot more than just call #to_a though, and would be worth a Google search on its own.

Now, in your case, the Range class itself is already an Enumerable so you could just do:

(first..last).each do |v| 
  ...
end
Heliolatry answered 12/5, 2011 at 17:21 Comment(0)
T
3

It is called a splat operator. If you use it within certain positions like an argument position or an array, it will expand into its elements:

a = [1]
[*a, 3] # => [1, 3]
b = [1, 2]
[*b, 3] # => [1, 2, 3]

You can't use it bare or in ranges:

*a..3 # => error.
(*a..3) # => error.

When you have something that is not an array, it returns itself:

a = 1
[*a, 3] # => [1, 3]
Tumult answered 12/5, 2011 at 17:20 Comment(0)
B
3

[first..last] is an array containing only 1 range object. [*first..last] is an array containing the elements of that range having been sent in as an argument list. * works in the context of an argument list.

Brainwash answered 12/5, 2011 at 17:24 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.