出于向后兼容性的原因,是否有一种方法可以定义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}
请注意,从技术上讲,您也可以同时通过klass
和before
,或者根本不通过:
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'