Ruby可选参数

Ruby optional parameters

如果我定义这样的Ruby函数:

1
def ldap_get ( base_dn, filter, scope=LDAP::LDAP_SCOPE_SUBTREE, attrs=nil )

我如何才能称之为只提供前2个和最后一个参数?为什么不是

1
ldap_get( base_dn, filter, , X)

可能,或者如果可能,怎么做?


使用选项哈希几乎总是更好的。

1
2
3
4
5
6
def ldap_get(base_dn, filter, options = {})
  options[:scope] ||= LDAP::LDAP_SCOPE_SUBTREE
  ...
end

ldap_get(base_dn, filter, :attrs => X)


Ruby目前不可能做到这一点。不能将"空"属性传递给方法。你能得到的最接近的是通过零:

1
ldap_get(base_dn, filter, nil, X)

但是,这会将作用域设置为nil,而不是ldap::ldap_scope_子树。

您可以在方法中设置默认值:

1
2
3
4
def ldap_get(base_dn, filter, scope = nil, attrs = nil)
  scope ||= LDAP::LDAP_SCOPE_SUBTREE
  ... do something ...
end

现在,如果您如上所述调用该方法,那么行为将如您所期望的那样。


随着时间的推移,第2版Ruby支持命名参数:

1
2
3
4
5
def ldap_get ( base_dn, filter, scope:"some_scope", attrs: nil )
  p attrs
end

ldap_get("first_arg","second_arg", attrs:"attr1, attr2") # =>"attr1, attr2"


不可能按照您定义的ldap_get的方式进行。但是,如果您这样定义ldap_get

1
def ldap_get ( base_dn, filter, attrs=nil, scope=LDAP::LDAP_SCOPE_SUBTREE )

现在你可以:

1
ldap_get( base_dn, filter, X )

但是现在您有一个问题,您不能用前两个参数和最后一个参数来调用它(与以前的问题相同,但现在最后一个参数不同)。

其基本原理很简单:Ruby中的每个参数都不需要有一个默认值,所以您不能以指定的方式调用它。例如,在您的例子中,前两个参数没有默认值。


1)您不能重载方法(Ruby为什么不支持方法重载?)那么为什么不一起写一个新方法呢?

2)我使用splat操作符*为零或更长的数组解决了类似的问题。然后,如果我想传递一个我可以传递的参数,它被解释为一个数组,但是如果我想调用不带任何参数的方法,那么我不需要传递任何东西。参见Ruby编程语言第186/187页


最近我找到了解决这个问题的方法。我想在数组类中创建一个带有可选参数的方法,以保留或丢弃数组中的元素。

我模拟这个的方法是传递一个数组作为参数,然后检查该索引的值是否为零。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Array
  def ascii_to_text(params)
    param_len = params.length
    if param_len > 3 or param_len < 2 then raise"Invalid number of arguments #{param_len} for 2 || 3." end
    bottom  = params[0]
    top     = params[1]
    keep    = params[2]
    if keep.nil? == false
      if keep == 1
        self.map{|x| if x >= bottom and x <= top then x = x.chr else x = x.to_s end}
      else
        raise"Invalid option #{keep} at argument position 3 in #{p params}, must be 1 or nil"
      end
    else
      self.map{|x| if x >= bottom and x <= top then x = x.chr end}.compact
    end
  end
end

尝试使用不同参数的类方法:

1
2
array = [1, 2, 97, 98, 99]
p array.ascii_to_text([32, 126, 1]) # Convert all ASCII values of 32-126 to their chr value otherwise keep it the same (That's what the optional 1 is for)

输出:["1","2","a","b","c"]

好吧,冷静点,按计划工作。现在,让我们检查一下,如果不传入数组中的第三个参数选项(1),会发生什么。

1
2
array = [1, 2, 97, 98, 99]
p array.ascii_to_text([32, 126]) # Convert all ASCII values of 32-126 to their chr value else remove it (1 isn't a parameter option)

输出:["a","b","c"]

如您所见,数组中的第三个选项已被删除,因此在方法中启动一个不同的部分,并删除不在我们范围内的所有ASCII值(32-126)

或者,我们可以在参数中发布值为nil。类似于以下代码块:

1
2
3
4
5
6
def ascii_to_text(top, bottom, keep = nil)
  if keep.nil?
    self.map{|x| if x >= bottom and x <= top then x = x.chr end}.compact
  else
    self.map{|x| if x >= bottom and x <= top then x = x.chr else x = x.to_s end}
end


您可以使用部分应用程序实现这一点,尽管使用命名变量肯定会导致代码更可读。JohnResig在2008年写了一篇关于如何使用javascript的博客文章:http://ejohn.org/blog/partial-functions-in-javascript/

1
2
3
4
5
6
7
8
9
10
Function.prototype.partial = function(){
  var fn = this, args = Array.prototype.slice.call(arguments);
  return function(){
    var arg = 0;
    for ( var i = 0; i < args.length && arg < arguments.length; i++ )
      if ( args[i] === undefined )
        args[i] = arguments[arg++];
    return fn.apply(this, args);
  };
};

在Ruby中应用相同的原则是可能的(除了原型继承)。


有可能:)只需更改定义

1
def ldap_get ( base_dn, filter, scope=LDAP::LDAP_SCOPE_SUBTREE, attrs=nil )

1
2
def ldap_get ( base_dn, filter, *param_array, attrs=nil )
scope = param_array.first || LDAP::LDAP_SCOPE_SUBTREE

作用域现在将位于数组的第一位。当您提供3个参数时,那么您将分配基本u dn、filter和attrs,参数u数组将为[]当4个或更多参数时,参数数组将为[参数1或更多,和更多]

缺点是…这是个不清楚的解决方案,真的很难看。这是为了回答在ruby()中函数调用的中间可以使用ommit参数。

您必须做的另一件事是重写范围的默认值。