Why Is the Splat Used Inside an Array Definition Here

Why is the splat used inside an array definition here?

It creates a single flat array for catch

I'm not sure anyone completely understands the splat operator. Many times it removes one level of "arrayness", but it won't remove the last level.

It is possible to get it in this one case, at least. It creates a single level of array for the catch parameter regardless of whether catch is a single number or an array of numbers.

>> t = [*404]
=> [404]
>> t = [*[404,405,406]]
=> [404, 405, 406]

What does this `*` symbol in this ruby snippet mean? (splat?)

Suppose I had some method in Ruby:

def a_method(a,b,c)
"#{a} #{b} #{c}"
end

and an array:

arr = [1,2,3]

I want to pass the 3 elements of the array into the method. This will produce an error:

a_method(arr) # wrong number of arguments

So what do I do? I could certainly do:

a_method(arr[0], arr[1], arr[2])

but there's an easier way, using the * "splat" operator:

a_method(*arr)

Basically, you'll achieve the same effect as above. Think of it like a "shortcut" that "converts" each array element into a method argument when being used when you call a method. This splat operator is a bit complicated to understand because it behaves differently when being used in different places (you have lots of useful articles on the topic).

In your example, basically, after the following expression is done:

items.map { |item| [item, item.to_s+' !!'] }.flatten

it produces:

[:one, "one !!", :two, "two !!", :three, "three !!"]

and this data is being passed to the Hash method using the "splat" operator, because Hash will not accept a single array as an argument:

arr = ['a', 'b', 'c', 'd']
p Hash{arr} #=> error, wrong number of arguments
p Hash[*arr] #=> {"a"=>"b", "c"=>"d"}

Explanation of splat

You seem like you get the mechanism and how/what they do but are struggling with what you would use it for. I get that.

I find them useful for things where I need to pass an unknown number of arguments and don't want to have to bother constructing an array first before passing it in when working with the function interactively.

for instance:

func geturls(urls::Vector)
# some code to retrieve URL's from the network
end
geturls(urls...) = geturls([urls...])

# slightly nicer to type than building up an array first then passing it in.
geturls("http://google.com", "http://facebook.com")

# when we already have a vector we can pass that in as well since julia has method dispatch
geturls(urlvector)

So a few things to note. Splat's allow you to turn an iterable into an array and vice versa. See the [urls...] bit above? Julia turns that into a Vector with the urls tuple expanded which turns out to be much more useful than the argument splatting itself in my experience.

This is just 1 example of where they've proved useful to me. As you use julia you'll run across more.

It's mostly there to aid in designing api's that feel natural to use.

What's the splat doing here?

is there a way to do this without the splat so it's less confusing?

Since a,b = [c,d] is the same as a,b = *[c,d] and splat calls to_a on its operand when it's not an array you could simply call to_a explicitly and not need the splat:

match, text, number = "foobar 123".match(/([A-z]*) ([0-9]*)/).to_a

Don't know whether that's less confusing, but it's splatless.

What does the (unary) * operator do in this Ruby code?

The * is the splat operator.

It expands an Array into a list of arguments, in this case a list of arguments to the Hash.[] method. (To be more precise, it expands any object that responds to to_ary/to_a, or to_a in Ruby 1.9.)

To illustrate, the following two statements are equal:

method arg1, arg2, arg3
method *[arg1, arg2, arg3]

It can also be used in a different context, to catch all remaining method arguments in a method definition. In that case, it does not expand, but combine:

def method2(*args)  # args will hold Array of all arguments
end

Some more detailed information here.

Why is splat argument in ruby not used all the time?

The splat is great when the method you are writing has a genuine need to have an arbitrary number of arguments, for a method such as Hash#values_at.

In general though, if a method actually requires a fixed number of arguments it's a lot clearer to have named arguments than to pass arrays around and having to remember which position serves which purpose. For example:

def File.rename(old_name, new_name)
...
end

is clearer than:

def File.rename(*names)
...
end

You'd have to read the documentation to know whether the old name was first or second. Inside the method, File.rename would need to implement error handling around whether you had passed the correct number of arguments. So unless you need the splat, "normal" arguments are usually clearer.

Keyword arguments (new in ruby 2.0) can be even clearer at point of usage, although their use in the standard library is not yet widespread.

Where is it legal to use ruby splat operator?

First, precedence isn't an issue here, because foo = bar || (*zap) works no better. The general rule of thumb is that you cannot perform additional operations on a splat. Even something as simple as foo = (*zap) is invalid. This applies to 1.9 as well.

Having said that, what do you expect foo = bar || *zap to do, if it worked, that is different than foo = bar || zap? Even in a case like a, b = bar || *zap (which also doesn't work), a, b = bar || zap accomplishes what I'd assume would be the same thing.

The only situation where this might make any sense is something like a, b = foo, bar || *zap. You should find that most cases where you would want to use this are covered by a, b = foo, *(bar || zap). If that doesn't cover your case, you should probably ask yourself what you really hope to accomplish by writing such an ugly construct.


EDIT:

In response to your comments, *zap || bar is equivalent to *(zap || bar). This demonstrates how low the splat's precedence is. Exactly how low is it? The best answer I can give you is "pretty low".

For an interesting example, though, consider a method foo which takes three arguments:

def foo(a, b, c)
#important stuff happens here!
end

foo(*bar = [1, 2, 3]) will splat after the assignment and set the arguments to 1, 2, and 3 respectively. Compare that with foo((*bar = [1, 2, 3])) which will complain about having the wrong number of arguments (1 for 3).

What does * mean at the beginning of a range?

As it is stated in the documentation:

You can turn an Array into an argument list with * (or splat) operator:

arguments = [1, 2, 3]
my_method(*arguments)

The same might be done for Range:

arguments = 1..3
my_method(*arguments) # essentially the same as my_method(1, 2, 3)

Also splat operator is allowed before ranges inside the array declaration to implicitly convert Range to Array:

[*1..3]
#⇒ [1, 2, 3]


Related Topics



Leave a reply



Submit