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



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

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

其中CCD_ 2是关键字自变量。

我目前有

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

下面是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“

您可以将方法定义为:

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

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

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' }

如果你想知道是klass还是before通过了,你必须使用旧的"指定一个标志作为副作用技巧":

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'

最新更新