Ruby optional parameters

如果我像这样定义一个 Ruby 函数:

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

How can I call it supplying only the first 2 and the last args? Why isn't something like

ldap_get( base_dn, filter, , X)

可能的或者如果可能的话,怎样才能做到呢?

180849 次浏览

这是不可能做到的方式,你已经定义的 ldap_get。然而,如果你定义的 ldap_get这样:

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

现在你可以:

ldap_get( base_dn, filter, X )

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

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

现在对 Ruby 来说不可能。不能将‘ em pty’属性传递给方法。你所能得到的最接近的结果就是:

ldap_get(base_dn, filter, nil, X)

但是,这将把范围设置为 nil,而不是 LDAP: : LDAP _ SCOPE _ SUBTREE。

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

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

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

使用选项散列几乎总是更好。

def ldap_get(base_dn, filter, options = {})
options[:scope] ||= LDAP::LDAP_SCOPE_SUBTREE
...
end


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

这是可能的:) Just change definition

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

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

作用域现在将放在数组的第一个位置。 当您提供3个参数时,那么您将分配 base _ dn、 filter 和 attrs,param _ array 将为[] 当有4个或更多参数时,param _ array 将是[ argument1,或 _ more,和 _ more ]

缺点是... 解决方案不明确,真的很难看。这是为了回答在 Ruby 中函数调用的中间可以省略参数:)

Another thing you have to do is to rewrite default value of scope.

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

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

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

用不同的参数试验我们的 class 方法:

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"]

Okay, cool that works as planned. Now let's check and see what happens if we don't pass in the the third parameter option (1) in the array.

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 发出。它看起来类似于下面的代码块:

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

1)你不能重载这个方法(为什么 Ruby 不支持方法重载?) ,那么为什么不写一个新的方法呢?

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

时间已经过去了,自从版本2 Ruby 支持命名参数:

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"

您可以对部分应用程序执行此操作,尽管使用命名变量肯定会提高代码的可读性。John Resig 在2008年写了一篇关于如何在 JavaScript: http://ejohn.org/blog/partial-functions-in-javascript/中实现的博客文章

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 中应用同样的原则也是可能的(原型继承除外)。