问题
If I have the following method in Ruby:
def foo(arg1, arg2 = "bar")
puts arg1
puts arg2
end
Is there a way of determining if a user passed a value in for arg2
within the method? Obviously I could just add if arg2 == "bar"
to the method, but this doesn't catch the case where the user manually passed in "bar"
herself. Of course, I could set the default to be something that no user would ever pass in, but then that gets pretty ugly pretty quickly.
Is there anything elegant out there?
回答1:
I am not sure this is possible. Consider the following:
class Test
def method(a,b=1)
local_variables.each do |var|
puts "Setting #{var} to #{eval var.to_s}"
end
end
end
And then try to call method
on an instance of Test
:
?> t.method(1)
Setting a to 1
Setting b to 1
=> [:a, :b]
?> t.method(1,2)
Setting a to 1
Setting b to 2
=> [:a, :b]
?> t.method(1,1)
Setting a to 1
Setting b to 1
=> [:a, :b]
Those do not distinguish how the method was called.
So I would suggest @sawa's approach mixed with the following:
raise ArgumentError, "Too many arguments" if args.length > 2
since you seem to want to limit the parameters to 2 and @sawa's approach allows for an undefined number.
This is an unorthodox approach to an unorthodox requirement, so just make sure the clients of your method are aware of how the API works.
回答2:
def foo(arg1, arg2 = (arg2_not_passed = true; "bar"))
puts arg1
puts arg2
puts 'arg2 was passed' unless arg2_not_passed
end
回答3:
def foo(*args)
case args.length
when 1
# arg2 was not given
arg1, arg2 = args.first, "bar"
...
when 2
# arg2 was given
arg1, arg2 = args
...
else
raise ArgumentError
end
end
回答4:
One way (not elegant but fun) is to make the default value such that it has a unique object_id
. It wil not work if default value is an immutable object like integer.
def c
@c ||= "bar"
end
def fun(a,b = c)
puts b.object_id == c.object_id ? "default" : "user"
end
fun "foo"
#=> "default"
fun "foo", "bar"
#=> "user"
回答5:
I have a solution! Suppose we want:
def foo(arg1, arg2 = 42)
Then we would instead write:
def foo(arg1, arg2 = [42,KEY])
If agr2 == [42,KEY]
, we conclude that the calling method did not pass a second argument, in which case the default of 42
applies. If arg2
is anything else, arg2
was passed. Simple!
I know what you're thinking: what if the calling program were to actually pass [42,KEY]
? I have that figured out too: KEY = rand(100000000000000)
.
I would like to add one more thing. I am in principal opposed to doing so, but I know if I don't I will receive so many downvotes that my reputation will go negative. So here it is: :-)
来源:https://stackoverflow.com/questions/19472413/how-to-determine-if-optional-arguments-are-passed-in-ruby