Keyword Arguments Unpacking (Splat) in Ruby

Keyword arguments unpacking (splat) in Ruby

That is a Ruby's bug that has been reported several times (for example here by me) but has not been fixed.

I guess that since the keyword argument feature has been introduced, the double splat syntax has become murky, and that is the indirect cause of this bug. I heard that Matz is considering of introducing a new syntax in some future version of Ruby to distinguish hashes and keyword arguments.

optional key word argument needed after splat argument when sub-elements are not arrays

The splat operator splits the array into arguments.

However if you wrap it in an array it works again but now it's an array inside an array and still treated as a single argument passed to your method.

call([*scores]) #no error

But also to illustrate why you got the error look what happens here:

def call(*scores, alpha: nil)
puts scores.inspect
end

call(*scores[0]) #=> #[[:a, 1]]

UPDATED: Thanks to @Stefan, the reason for the error is actually that your method accepts keyword arguments, which is apparently a known bug. See Keyword arguments unpacking (splat) in Ruby

The reason your last example works is that by passing a 2nd argument to your method, the splat handles the first argument as an array instead of trying to split it up into 2 arguments.

Fore more see Ruby, Source Code of Splat?

Also see https://www.rubyguides.com/2018/07/ruby-operators/#Ruby_Splat_Operator

Name this python/ruby language construct (using array values to satisfy function parameters)

The Python docs call this Unpacking Argument Lists. It's a pretty handy feature. In Python, you can also use a double asterisk (**) to unpack a dictionary (hash) into keyword arguments. They also work in reverse. I can define a function like this:

def sum(*args):
result = 0
for a in args:
result += a
return result

sum(1,2)
sum(9,5,7,8)
sum(1.7,2.3,8.9,3.4)

To pack all arguments into an arbitrarily sized list.

Splat operators in JavaScript, equivalent to *args and **kwargs in Python?

The closest idiom for *args would be

function func (a, b /*, *args*/) {
var star_args = Array.prototype.slice.call (arguments, func.length);
/* now star_args[0] is the first undeclared argument */
}

taking advantage of the fact that Function.length is the number of arguments given in the function definition.

You could package this up in a little helper routine like

function get_star_args (func, args) {
return Array.prototype.slice.call (args, func.length);
}

and then do

function func (a, b /*, *args*/) {
var star_args = get_star_args (func, arguments);
/* now star_args[0] is the first undeclared argument */
}

If you're in the mood for syntactic sugar, write a function which transforms one function into another one which is called with required and optional arguments, and passes the required arguments along, with any additional optional arguments as an array in final position:

function argsify(fn){
return function(){
var args_in = Array.prototype.slice.call (arguments); //args called with
var required = args_in.slice (0,fn.length-1); //take first n
var optional = args_in.slice (fn.length-1); //take remaining optional
var args_out = required; //args to call with
args_out.push (optional); //with optionals as array
return fn.apply (0, args_out);
};
}

Use this as follows:

// original function
function myfunc (a, b, star_args) {
console.log (a, b, star_args[0]); // will display 1, 2, 3
}

// argsify it
var argsified_myfunc = argsify (myfunc);

// call argsified function
argsified_myfunc (1, 2, 3);

Then again, you could just skip all this mumbo jumbo if you are willing to ask the caller to pass the optional arguments as an array to start with:

myfunc (1, 2, [3]);

There is really no analogous solution for **kwargs, since JS has no keyword arguments. Instead, just ask the caller to pass the optional arguments in as an object:

function myfunc (a, b, starstar_kwargs) {
console.log (a, b, starstar_kwargs.x);
}

myfunc (1, 2, {x:3});

ES6 Update

For completeness, let me add that ES6 solves this problem with the rest parameter feature. See Javascript - '...' meaning

Go Unpacking Array As Arguments

You can use a vararg syntax similar to C:

package main
import "fmt"

func my_func( args ...int) int {
sum := 0
for _,v := range args {
sum = sum + v
}

return sum;
}

func main() {
arr := []int{2,4}
sum := my_func(arr...)
fmt.Println("Sum is ", sum)
}

Now you can sum as many things as you'd like. Notice the important ... after when you call the my_func function.

Running example: http://ideone.com/8htWfx

proper name for python * operator?

In Ruby and Perl 6 this has been called "splat", and I think most people from
those communities will figure out what you mean if you call it that.

The Python tutorial uses the phrase "unpacking argument lists", which is
long and descriptive.

It is also referred to as iterable unpacking, or in the case of **,
dictionary unpacking.

Skipping extra keyword arguments in Ruby

If you don't want to write the other as in **other, you can omit it.

def method(one: 1, two: 2, **)
[one, two]
end


Related Topics



Leave a reply



Submit