如果改为传递定位参数,如何设置关键字参数

时间:2019-10-24 08:00:00

标签: ruby

出于向后兼容的原因,是否有一种方法可以定义my方法,因此以下两个调用是相同的?

my(1, A)
my(1, before: A)

其中before是关键字参数。

我目前有

def my(n, klass)
 puts "#{n} and #{klass}"
end

现在我需要能够调用my(1, before: A)并获得相同的结果(即“ 1和A”)

还可以使用第二个关键字参数,即my

来调用my(1, before: A, after: B)方法
def my(n, before, after: nil)
 puts "#{n} and #{before} but not #{after}"
end

3 个答案:

答案 0 :(得分:4)

下面是MCVE及其用法示例。

def my(after = nil, **args)
  after ||= args[:after]
  before = args[:before]
  puts "after = “#{after}” | before = “#{before}“"
end

my "A"
#⇒ after = “A” | before = ““
my after: "A"
#⇒ after = “A” | before = ““
my before: "B"
#⇒ after = “” | before = “B“

答案 1 :(得分:2)

您可以将方法定义为:

def my(n, klass = nil, before: nil)
  p n: n, klass: klass, before: before
end

这允许您传递位置参数或关键字参数:

my(1, A)
#=> {:n=>1, :klass=>A, :before=>nil}

my(1, before: A)
#=> {:n=>1, :klass=>nil, :before=>A}

在该方法中,您可以将一个变量分配给另一个变量,即:

def my(n, klass = nil, before: nil)
  klass ||= before
  p n: n, klass: klass
end

my(1, A)         #=> {:n=>1, :klass=>A}
my(1, before: A) #=> {:n=>1, :klass=>A}

或:

def my(n, klass = nil, before: nil)
  before ||= klass
  p n: n, before: before
end

my(1, A)         #=> {:n=>1, :before=>A}
my(1, before: A) #=> {:n=>1, :before=>A}

后者可能更干净,另外还有一个after关键字:

def my(n, klass = nil, before: nil, after: nil)
  before ||= klass
  p n: n, before: before, after: after
end

my(1, A, after: B)         #=> {:n=>1, :before=>A, :after=>B}
my(1, before: A, after: B) #=> {:n=>1, :before=>A, :after=>B}

请注意,从技术上讲,您还可以同时通过klassbefore或全部不通过:

def my(n, klass = nil, before: nil)
  p n: n, klass: klass, before: before
end

my(1, A, before: A) #=> {:n=>1, :klass=>A, :before=>A}
my(1)               #=> {:n=>1, :klass=>nil, :before=>nil}

在这种情况下,您可能想提出一个ArgumentError

答案 2 :(得分:0)

很遗憾,您的要求不是很清楚,但是我认为这应该可以满足您的要求:

def my(n, klass=nil, before: klass, after: nil)
  local_variables.map {|var| next var.to_sym, binding.local_variable_get(var) }.to_h
end

my 1
#=> { n: 1, klass: nil, before: nil, after: nil }

my 1, 'A'
#=> { n: 1, klass: 'A', before: 'A', after: nil }

my 1, before: 'A'
#=> { n: 1, klass: nil, before: 'A', after: nil }

my 1, before: 'A', after: 'B'
#=> { n: 1, klass: nil, before: 'A', after: 'B' }

my 1, 'A', after: 'B'
#=> { n: 1, klass: 'A', before: 'A', after: 'B' }

my 1, 'A', before: 'B', after: 'C'
#=> { n: 1, klass: 'A', before: 'B', after: 'C' }

如果您想知道是否通过了klassbefore,则必须使用旧的“将标志分配为副作用”:

def my(n, klass=(klass_not_passed = true; nil), before: (before_not_passed = true; klass), after: nil)
  raise ArgumentError, "You may only pass one of `klass` or `before` but you passed `klass == #{klass.inspect}` and `before == #{before.inspect}`" unless klass_not_passed || before_not_passed

  local_variables.map {|var| next var.to_sym, binding.local_variable_get(var) }.to_h
end

my 1
#=> { n: 1, klass: nil, before: nil, after: nil, klass_not_passed: true, before_not_passed: true }

my 1, 'A'
#=> { n: 1, klass: 'A', before: 'A', after: nil, klass_not_passed: nil, before_not_passed: true }

my 1, before: 'A'
#=> { n: 1, klass: nil, before: 'A', after: nil, klass_not_passed: true, before_not_passed: nil }

my 1, before: 'A', after: 'B'
#=> { n: 1, klass: nil, before: 'A', after: 'B', klass_not_passed: true, before_not_passed: nil }

my 1, 'A', after: 'B'
#=> { n: 1, klass: 'A', before: 'A', after: 'B', klass_not_passed: nil, before_not_passed: true }

my 1, 'A', before: 'B', after: 'C'
# ArgumentError: You may only pass one of `klass` or `before` but you passed `klass == "A"` and `before == "B"`
# from test.rb:36:in `my'