Optional argument after splat

Here is my program:

def calculate(*numbers, options = {}) add(numbers) if options[:add] subtract(numbers) if options[:add] == false end def add(*numbers) numbers.reduce(:+) end def subtract(*numbers) numbers.reduce(:-) end p calculate(1,2) 

In line 1 he complains

tests.rb: 1: syntax error, unexpected '=', pending ')'
def calculate (* numbers, options = {})
________________________________________________ ^
[Finished at 0.1 with exit code 1]

I thought this might be the problem with the default values ​​in Ruby, because before v1.9 you had to have all the default values ​​in order - but that should not be a problem, because my version

ruby 2.0.0p195 (2013-05-14) [i386-mingw32]

I tried wrapping spaces all over, because Ruby seems to be especially involved with these things when it comes to methods, but without cubes.

Could this be my *numbers symbol variable?

+7
source share
6 answers

Thanks @maerics and @JorgWMittag -

When you have splat, it saves all the arguments, so I didn’t like my second argument “options”. I fixed this problem by changing the arguments to -

 def calculate(*arguments) options = arguments[-1].is_a?(Hash) ? arguments.pop : {} options[:add] = true if options.empty? return add(*arguments) if options[:add] return subtract(*arguments) if options[:subtract] end 
+8
source

You cannot have additional parameters after splat.

Split means “use all remaining arguments”, but then you provide an optional argument, so how could the interpreter know if the last argument is part of “numbers” or additional “options”?

+9
source

You may have required arguments after the splat argument. Optional arguments must come before the splat character.

The pseudo-regular expression for parameter lists in Ruby looks something like this:

 mand* opt* splat? mand* (mand_kw | opt_kw)* kwsplat? block? 

Here is an example:

 def foo(m1, m2, o1=:o1, o2=:o2, *splat, m3, m4, ok1: :ok1, mk1:, mk2:, ok2: :ok2, **ksplat, &blk) Hash[local_variables.map {|var| [var, eval(var.to_s)] }] end method(:foo).arity # => -5 method(:foo).parameters # => [[:req, :m1], [:req, :m2], [:opt, :o1], [:opt, :o2], [:rest, :splat], # [:req, :m3], [:req, :m4], [:keyreq, :mk1], [:keyreq, :mk2], # [:key, :ok1], [:key, :ok2], [:keyrest, :ksplat], [:block, :blk]] foo(1, 2, 3, 4) # ArgumentError: missing keywords: mk1, mk2 foo(1, 2, 3, mk1: 4, mk2: 5) # ArgumentError: wrong number of arguments (3 for 4+) foo(1, 2, 3, 4, mk1: 5, mk2: 6) # => { m1: 1, m2: 2, o1: :o1, o2: :o2, splat: [], m3: 3, m4: 4, # ok1: :ok1, mk1: 5, mk2: 6, ok2: :ok2, ksplat: {}, # blk: nil } foo(1, 2, 3, 4, 5, mk1: 6, mk2: 7) # => { m1: 1, m2: 2, o1: 3, o2: :o2, splat: [], m3: 4, m4: 5, # ok1: :ok1, mk1: 6, mk2: 7, ok2: :ok2, ksplat: {}, # blk: nil } foo(1, 2, 3, 4, 5, 6, mk1: 7, mk2: 8) # => { m1: 1, m2: 2, o1: 3, o2: 4, splat: [], m3: 5, m4: 6, # ok1: :ok1, mk1: 7, mk2: 8, ok2: :ok2, ksplat: {}, # blk: nil } foo(1, 2, 3, 4, 5, 6, 7, mk1: 8, mk2: 9) # => { m1: 1, m2: 2, o1: 3, o2: 4, splat: [5], m3: 6, m4: 7, # ok1: :ok1, mk1: 8, mk2: 9, ok2: :ok2, ksplat: {}, # blk: nil } foo(1, 2, 3, 4, 5, 6, 7, 8, mk1: 9, mk2: 10) # => { m1: 1, m2: 2, o1: 3, o2: 4, splat: [5, 6], m3: 7, m4: 8, # ok1: :ok1, mk1: 9, mk2: 10, ok2: :ok2, ksplat: {}, # blk: nil } foo(1, 2, 3, 4, 5, 6, 7, 8, ok1: 9, mk1: 10, mk2: 11) # => { m1: 1, m2: 2, o1: 3, o2: 4, splat: [5, 6], m3: 7, m4: 8, # ok1: 9, mk1: 10, mk2: 11, ok2: :ok2, ksplat: {}, # blk: nil } foo(1, 2, 3, 4, 5, 6, 7, 8, ok1: 9, mk1: 10, mk2: 11, ok2: 12) # => { m1: 1, m2: 2, o1: 3, o2: 4, splat: [5, 6], m3: 7, m4: 8, # ok1: 9, mk1: 10, mk2: 11, ok2: 12, ksplat: {}, # blk: nil } foo(1, 2, 3, 4, 5, 6, 7, 8, ok1: 9, mk1: 10, mk2: 11, ok2: 12, k3: 13) # => { m1: 1, m2: 2, o1: 3, o2: 4, splat: [5, 6], m3: 7, m4: 8, # ok1: 9, mk1: 10, mk2: 11, ok2: 12, ksplat: {k3: 13}, # blk: nil } foo(1, 2, 3, 4, 5, 6, 7, 8, ok1: 9, mk1: 10, mk2: 11, ok2: 12, k3: 13, k4: 14) # => { m1: 1, m2: 2, o1: 3, o2: 4, splat: [5, 6], m3: 7, m4: 8, # ok1: 9, mk1: 10, mk2: 11, ok2: 12, ksplat: {k3: 13, k4: 14}, # blk: nil } foo(1, 2, 3, 4, 5, 6, 7, 8, ok1: 9, ok2: 10, mk1: 11, mk2: 12, k3: 13, k4: 14) do 15 end # => { m1: 1, m2: 2, o1: 3, o2: 4, splat: [5, 6], m3: 7, m4: 8, # ok1: 9, mk1: 10, mk2: 11, ok2: 12, ksplat: {k3: 13, k4: 14}, # blk: #<Proc: 0xdeadbeefc00l42@ (irb):15> } 
+6
source
 def calculate( *numbers, add: true ) add ? add( *numbers ) : subtract( *numbers ) end def add *numbers; numbers.reduce( 0, :+) end def subtract n1, n2; n1 - n2 end calculate 1, 2 #=> 3 calculate 3, 1, add: false #=> 2 
+3
source
+1
source

Actually, from Ruby 2.0 you can accomplish this using keyword arguments . If you define a "calculate" method as follows:

 def calculate(a, *b, **options) return a + b.inject(0, :+) if options[:add] return a + b.inject(0, :-) if options[:subtract] return 0 end 

Then you can call this method with this:

 calculate(3, 4, -5, 3, -8, add: true) 

And you get -3 as a result.

+1
source

All Articles