为啥 Enumerable#detect 需要 Proc/lambda?

Posted

技术标签:

【中文标题】为啥 Enumerable#detect 需要 Proc/lambda?【英文标题】:Why does Enumerable#detect need a Proc/lambda?为什么 Enumerable#detect 需要 Proc/lambda? 【发布时间】:2014-01-19 22:16:59 【问题描述】:

Enumerable#detect 返回数组的第一个值,其中块的计算结果为true。它有一个可选参数,需要响应call 并在这种情况下被调用,返回它的值。所以,

(1..10).detect(lambda "none" )|i| i == 11 #=> "none"

为什么我们需要 lambda?为什么我们不只传递默认值本身,因为(在我的测试中)lambda 无论如何都不能有任何参数?像这样:

(1..10).detect("none")|i| i == 11 #=> "none"

【问题讨论】:

你必须放一个对象,它应该响应#call 方法。它可以是 ProcMethod 对象。 考虑(1..10).detect |i| i == 11 || "none" 不错的解决方法,韦恩! 【参考方案1】:

与 Ruby 中的所有内容一样,“最小意外原则”适用。当然,这并不是说“对你来说最不意外”。 Matz 对what it actually means 相当坦率:

每个人都有自己的背景。有人可能来自 Python,也有人可能来自 Perl,他们可能会对语言的不同方面感到惊讶。然后他们来找我说,“我对语言的这个特性感到惊讶,所以 Ruby 违反了最小意外原则。”等待。等待。最小意外原则不仅适用于您。最小意外原则意味着最小我的意外原则。也就是你学好 Ruby 之后的最小意外原则。例如,在开始设计 Ruby 之前,我是一名 C++ 程序员。我专门用 C++ 编程了两三年。经过两年的 C++ 编程,它仍然让我感到惊讶。

所以,这里的理性实际上是任何人的猜测。

一种可能性是它允许或与您希望有条件地运行昂贵的东西的用例保持一致:

arr.detect(lambda  do_something_expensive )  |i| is_i_ok? i 

或者正如@majioa 所暗示的,也许是传递一个方法:

arr.detect(method(:some_method))  |i| is_i_ok? i 

【讨论】:

我读了这个段落,我喜欢这个段落.. +1 从互联网上获取它。 传递一个方法对我来说最有意义,因为我从未见过匿名函数用于传递异常。不过,这可能是我的对最不意外的解释。【参考方案2】:

接受可调用对象允许“惰性”和通用解决方案,例如在您想做一些昂贵的事情、引发异常等情况下......

不过,我看不出detect 不能接受不可调用参数的原因,尤其是现在在Ruby 2.1 中很容易创建cheap frozen litterals。为此,我打开了feature request。

【讨论】:

感谢您发出请求。【参考方案3】:

这可能是因为您可以从输入中生成适当的结果。然后你可以做类似的事情

arr = (1..10).to_a
arr.detect(lambda arr.length )|i| i == 11 #=> 10

正如您所说,无论如何,使用 lambda 返回一个常量值非常容易。

【讨论】:

【参考方案4】:

真的是个有趣的问题。我可以理解为什么作者通过方法调用添加了该功能,您可以将包含Method 对象或类似对象的method 变量作为参数传递。我认为这只是:detect 方法的自愿解决方案,因为它可以很容易地添加传递参数的类型来选择它是否是Method

我已经重新验证了示例,并得到:

(1..10).detect(proc 'wqw')   |i| i % 5 == 0 and i % 7 == 0    #=> nil
# => "wqw"
(1..10).detect('wqw')   |i| i % 5 == 0 and i % 7 == 0    #=> nil
# NoMethodError: undefined method `call' for "wqw":String

这太棒了。 =)

【讨论】:

【参考方案5】:

使用 lambda 的最佳用例是引发自定义异常

arr = (1..10).to_a
arr.detect(lambda raise "not found" )|i| i == 11 #=> RuntimeError: not found

因此,由于 K 是微不足道的(只是用 -> 包围),因此检查后备行为没有多大意义。

传递 &-ed 符号而不是块的类似情况实际上根本不相似,因为在这种情况下,它表示将在可枚举项上调用某些东西。

【讨论】:

以上是关于为啥 Enumerable#detect 需要 Proc/lambda?的主要内容,如果未能解决你的问题,请参考以下文章

为啥 Enumerable.Range 实现 IDisposable?

为啥重复 Enumerable 到 Observable 转换块

为啥 Enumerable 在 Ruby 中没有长度属性?

为啥 Enumerable 不实现 IEnumerable?

为啥 Enumerable 中的方法返回 Enumerator?

为啥 Enumerable.Range 比直接 yield 循环快?