检查是否未传递任何参数

6

这里是一些代码:

$ 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

问题是,是否有办法区分没有参数和传递了一个nil参数? 更新 我决定添加一个在javascript中的用例,希望能让事情更加清晰:
someProp: function(value) {
    if (arguments.length) {
        this._someProp = value;
    }
    return this._someProp;
}

如果p1默认为nil,那么传递nil不就等同于不传吗? - Cereal
@Cereal 请看一下这个问题,我添加了一个用例。因此,如果我无法区分没有值和nil,我就无法将变量的值设置为nil。有人可能会说我应该选择不同的默认值,但我认为在这种情况下nil是最合适的值。另外,也许你不能期望变量不取某些值。 - x-yuri
2个回答

11

一般来说,有三种常用方法。其中一种方法是使用默认值来设置另一个变量,指示是否已评估默认值:

def f(p1 = (no_argument_passed = true; nil))
  'no arguments passed' if no_argument_passed
end

f      # => 'no arguments passed'
f(nil) # => nil
第二种方法是使用一些只在方法内部已知的对象作为默认值,这样外部人员就无法传递该对象:
-> {
  undefined = BasicObject.new
  define_method(:f) do |p1 = undefined|
    'no arguments passed' if undefined.equal?(p1)
  end
}.()

f      # => 'no arguments passed'
f(nil) # => nil
这两个方案,前者更符合惯用语。后者(实际上是它的变体)在 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

请注意,这需要您手动重新实现 Ruby 的参数数量检查。(而且我们仍然没有做对,因为这会在方法内引发异常,而 Ruby 会在调用 引发异常。) 这还需要您手动记录方法签名,因为自动生成文档的工具如 RDoc 或 YARD 会推断出任意数量的参数,而不是一个单独的可选参数。


第一种方式让我想起了lisp,如果我没记错的话。 - x-yuri
好吧,我可能错了,这让我想起了确定参数是否被提供的方法:(defun foo (a b &optional (c 3 c-supplied-p)) (list a b c c-supplied-p))(foo 1 2) ==> (1 2 3 NIL)(foo 1 2 3) ==> (1 2 3 T)(foo 1 2 4) ==> (1 2 4 T) - x-yuri

5

您可以请求使用扩展参数:

def f(*args)
  if args.empty?
    puts 'no parameters passed'
  else
    p1 = args[0]
    ...
  end
end

另一种选择是创建一个私有对象来指示未传递参数:

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接