How to Collapse Double Splat Arguments into Nothing

What does a double * (splat) operator do

Ruby 2.0 introduced keyword arguments, and ** acts like *, but for keyword arguments. It returns a Hash with key / value pairs.

For this code:

def foo(a, *b, **c)
[a, b, c]
end

Here's a demo:

> foo 10
=> [10, [], {}]
> foo 10, 20, 30
=> [10, [20, 30], {}]
> foo 10, 20, 30, d: 40, e: 50
=> [10, [20, 30], {:d=>40, :e=>50}]
> foo 10, d: 40, e: 50
=> [10, [], {:d=>40, :e=>50}]

Unexpected result with splat operator

Because the []= method applied to hash takes only one argument in addition to the key (which is put inside the [] part), and a splatted/expanded array, which is in general a sequence of values (which coincidentally happens to be a single element in this particular case) cannot be directly accepted as the argument as is splatted. So it is accepted by the argument of []= as an array after all.

In other words, an argument (of the []= method) must be an object, but splatted elements (such as :foo, :bar, :baz) are not an object. The only way to interpret them as an object is to put them back into an array (such as [:foo, :bar, :baz]).

Using the splat operator, you can do it like this:

hash.each_pair{|key, value| hash.[]= key, *value}

Map splat arguments over method parameters

def f(params,*args)
# elements to be assigned to splat parameter
splat = args.count - params.count + 1

# will throw an error if splat < 0 as that means not enough inputs given
params.map{ |p|

[ p[1] , ( p.first == :rest ? args.shift(splat) : args.shift ) ]

}
end

Examples

def splatter(x,*y,z)
# some code
end

f(method(:splatter).parameters, 1,2,3,4)
#=>[[:x, 1], [:y, [2, 3]], [:z, 4]]

def splatter(x,y,*z)
# some code
end

f(method(:splatter).parameters, 1,2,3,4)
# => [[:x, 1], [:y, 2], [:z, [3, 4]]]

def splatter(x,*z)
# some code
end

f(method(:splatter).parameters, 1)
# => [[:x, 1], [:z, []]]

Array equation explanation

To break anything down line by line one could use REPL:

*a, b = [1, 2, 3, 4]
#⇒ [1, 2, 3, 4]

a
#⇒ [1, 2, 3]

b
#⇒ 4

Using splat operator, we have decomposed the original array to new array and the single value. Now everything is crystal clear: a[b-2] which is a[2], which is in turn 3 (check a array.) and b is still 4.

3 + 4
#⇒ 7

Default arguments with *args and **kwargs

Just put the default arguments before the *args:

def foo(a, b=3, *args, **kwargs):

Now, b will be explicitly set if you pass it as a keyword argument or the second positional argument.

Examples:

foo(x) # a=x, b=3, args=(), kwargs={}
foo(x, y) # a=x, b=y, args=(), kwargs={}
foo(x, b=y) # a=x, b=y, args=(), kwargs={}
foo(x, y, z, k) # a=x, b=y, args=(z, k), kwargs={}
foo(x, c=y, d=k) # a=x, b=3, args=(), kwargs={'c': y, 'd': k}
foo(x, c=y, b=z, d=k) # a=x, b=z, args=(), kwargs={'c': y, 'd': k}

Note that, in particular, foo(x, y, b=z) doesn't work because b is assigned by position in that case.


This code works in Python 3 too. Putting the default arg after *args in Python 3 makes it a "keyword-only" argument that can only be specified by name, not by position. If you want a keyword-only argument in Python 2, you can use @mgilson's solution.

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.

C++ Boost::MPL fold example - wrong number of arguments

You are messing up the namespaces. Making a lot of symbols ambiguous.

Remove the using and the example works fine for me.

...
using namespace boost;

typedef mpl::vector<long,float,short,double,float,long,long double> types;
typedef mpl::fold<
types
, mpl::int_<0>
, mpl::if_< is_float<boost::mpl::_2>,boost::mpl::next<boost::mpl::_1>,boost::mpl::_1 >
>::type number_of_floats;
...


Related Topics



Leave a reply



Submit