这是一些代码:
$ cat 1.rb
#!/usr/bin/env ruby
def f p1 = nil
unless p1 # TODO
puts 'no parameters passed'
end
end
f
f nil
$ ./1.rb
no parameters passed
no parameters passed
Run Code Online (Sandbox Code Playgroud)
问题是,有没有办法区分没有参数和一个nil参数传递?
UPD
我决定添加一个用例,javascript以使事情更加清晰:
someProp: function(value) {
if (arguments.length) {
this._someProp = value;
}
return this._someProp;
}
Run Code Online (Sandbox Code Playgroud)
Jör*_*tag 10
一般使用有三种方法.一种方法是使用默认值设置另一个变量,指示是否评估了默认值:
def f(p1 = (no_argument_passed = true; nil))
'no arguments passed' if no_argument_passed
end
f # => 'no arguments passed'
f(nil) # => nil
Run Code Online (Sandbox Code Playgroud)
第二种方法是使用一些仅在方法内部已知的对象作为默认值,这样外人就不可能将该对象传递给:
-> {
undefined = BasicObject.new
define_method(:f) do |p1 = undefined|
'no arguments passed' if undefined.equal?(p1)
end
}.()
f # => 'no arguments passed'
f(nil) # => nil
Run Code Online (Sandbox Code Playgroud)
在这两个中,第一个更惯用.第二个(实际上,它的变体)在Rubinius内部使用,但我从未在其他任何地方遇到过它.
第三种解决方案是使用splat获取可变数量的参数:
def f(*ps)
num_args = ps.size
raise ArgumentError, "wrong number of arguments (#{num_args} for 0..1)" if num_args > 1
'no arguments passed' if num_args.zero?
end
f # => 'no arguments passed'
f(nil) # => nil
Run Code Online (Sandbox Code Playgroud)
请注意,这需要您手动重新实现Ruby的arity检查.(我们仍然没有得到它的权利,因为这引发异常里面的方法,而红宝石将在提高它调用站点.)这也需要您手动记录您的方法签名,因为自动记录生成如RDoc的或者YARD将推断任意数量的参数而不是单个可选参数.
您可以请求splat参数:
def f(*args)
if args.empty?
puts 'no parameters passed'
else
p1 = args[0]
...
end
end
Run Code Online (Sandbox Code Playgroud)
其他一些选项可能是让私有对象指示没有传递参数:
def initialize
@no_param_passed = Object.new
end
def f(p1 = @no_param_passed)
if p1 == @no_param_passed
puts 'no parameters passed'
end
end
Run Code Online (Sandbox Code Playgroud)