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
How to Run Ruby on Rails Applications on a Windows Box
Aws Cognito User Authentication Missing Required Parameter Srp_A
Check Method Call on Model Using Minitest
Preserving Case in Http Headers with Ruby's Net:Http
Async Requests Using Sinatra Streaming API
Interested in What the "<<" Does
Ruby - Calling Setters from Within an Object
How to Handle Method Order in Ruby
Writing an Activerecord Adapter
How to Force a Cucumber Scenario to Fail
Getting Headers from a Ruby Net::Http Request Before Making the Request
Subtract Values in Hash from Corresponding Values in Another Hash
Ruby Dot Parenthesis Call Syntax
Failure: Expected 0 to Be >= 1 on Ruby on Rails
Getting Google File Picker to Work with Drive.File Scope