9

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)

On line 1, it is complaining

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

I thought it might have been a problem with default values in Ruby, because before v1.9, you were required to have all default values in order - but this shouldn't be the issue because my version is

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

I've tried transposing the spaces all over, because ruby seems to be particular with those things when it comes to methods, but no dice.

Could it be my splat variable *numbers ?

4

6 回答 6

10

splat 后不能有可选参数。

splat 的意思是“用完所有剩余的参数”,但是您提供了一个可选参数,那么解释器如何知道最后一个参数是“数字”splat 还是可选“选项”的一部分?

于 2013-06-18T15:38:18.130 回答
8

感谢@maerics 和@JorgWMittag -

当你有一个 splat 时,它会保留所有参数,这就是为什么它不喜欢我的第二个“选项”参数。我通过改变我的论点来解决这个问题 -

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
于 2013-06-18T15:42:52.287 回答
6

在 splat 参数之后只能有强制参数。可选参数必须在 splat 之前。

Ruby 中参数列表的伪正则表达式是这样的:

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

这是一个例子:

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> }
于 2013-06-18T15:38:21.273 回答
3
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
于 2013-06-18T15:56:52.930 回答
1

查看extract_options!Rails 背后的工作原理。

http://simonecarletti.com/blog/2009/09/inside-ruby-on-rails-extract_options-from-arrays/

于 2015-05-18T22:44:49.753 回答
1

实际上,从Ruby 2.0 开始,您可以通过使用关键字参数来完成此操作。如果您像这样定义“计算”方法:

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

然后你可以用这个调用那个方法:

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

结果你会得到-3。

于 2015-05-31T22:36:47.067 回答