Enumerable#detect
returns the first value of an array where the block evaluates to true
. It has an optional argument that needs to respond to call
and is invoked in this case, returning its value. So,
(1..10).detect(lambda{ "none" }){|i| i == 11} #=> "none"
Why do we need the lambda? Why don't we just pass the default value itself, since (in my tests) the lambda can't have any parameters anyway? Like this:
(1..10).detect("none"){|i| i == 11} #=> "none"
As with all things in Ruby, the "principle of least surprise" applies. Which is not to say "least surprise for you" of course. Matz is quite candid about what it actually means:
Everyone has an individual background. Someone may come from Python, someone else may come from Perl, and they may be surprised by different aspects of the language. Then they come up to me and say, 'I was surprised by this feature of the language, so Ruby violates the principle of least surprise.' Wait. Wait. The principle of least surprise is not for you only. The principle of least surprise means principle of least my surprise. And it means the principle of least surprise after you learn Ruby very well. For example, I was a C++ programmer before I started designing Ruby. I programmed in C++ exclusively for two or three years. And after two years of C++ programming, it still surprises me.
So, the rational here is really anyone's guess.
One possibility is that it allows for or is consistent with use-cases where you want to conditionally run something expensive:
arr.detect(lambda { do_something_expensive }) { |i| is_i_ok? i }
Or as hinted by @majioa, perhaps to pass a method:
arr.detect(method(:some_method)) { |i| is_i_ok? i }
Accepting a callable object allows allows "lazy" and generic solutions, for example in cases where you'd want to do something expensive, raise an exception, etc...
I can't see a reason why detect
couldn't accept non callable arguments, though, especially now in Ruby 2.1 where it's easy to create cheap frozen litterals. I've opened a feature request to that effect.
It is probably so you can generate an appropiate result from the input. You can then do something like
arr = (1..10).to_a
arr.detect(lambda{ arr.length }){|i| i == 11} #=> 10
As you said, returning a constant value is very easy with a lambda anyway.
Really it is interestion question. I can understand why authors have added the feature with method call, you can just pass method
variable, containing a Method
object or similar, as an argument. I think it is simply voluntaristic solution for the :detect
method, because it could be easy to add switch on type of passed argument to select weither it is the Method
or not.
I've reverified the examples, and got:
(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
That is amazing. =)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With