Reputation: 167
I'm doing a bit of exploring. Concerning Ruby's
.find(ifnone = nil) { |obj| block }method: from reading the documentation, it seems to me that you are supposed to be able to pass a method as an argument that will be run in the case that there are no matches for the specified conditions.
It says:
"calls ifnone and returns its result when it is specified, or returns
nil
otherwise."
This seems to work with any method I create that already returns nil, say:
def message puts 'No match.' end
No match.
=>nil
If I use a method that does return something, say:
def message p 'No match.' end
I'll get:
"No match."
NoMethodError: undefined method `call' for "No match.":String
Would someone be kind enough to explain to me precisely what kind of arg/method is actually supposed to be passed to the find method here? Thanks.
Upvotes: 8
Views: 3537
Reputation: 2512
I'm glad you asked this question. I never thought about that argument for the find
method because I never really had to use it before. I instead always ignored it until you mentioned it here.
The argument you would pass to an enumerable, like find
, would be a lambda or proc. Rather than returning the default nil
if no matches were found, it would return the lambda/proc.
So a quick example:
nums = [1, 2, 3, 4]
nums.find(lambda {raise ArgumentError, "No matches found"}) { |num| num == 5 }
> ArgumentError: No matches found
Also similarly, you can pass a Proc as well..
nums = [1, 2, 3, 4]
arg = Proc.new {"No matches found"}
nums.find(arg) { |num| num == 5 }
> "No matches found"
Just a quick edit, you can return any value in the lambda or proc, whether it be raising an error or returning a value. I imagine raising an error and error handling is a common use though
Edit2: Removed link to an article explaining this method. Seems the blog post has been removed :(
Upvotes: 8
Reputation: 198294
NoMethodError: undefined method `call' for "No match.":String
This is a big hint. find
wants a callable object - i.e. something that responds to #call
. Since Ruby is duck-typed, all of these will work:
def message_method
puts "yo"
end
...find(method(:message_method)) { ... }
module MessageModule
def self.call
puts "yo"
end
end
...find(MessageModule) { ... }
class MessageClass
def call
puts "yo"
end
end
...find(MessageClass.new) { ... }
message_proc = Proc.new { puts "yo" }
...find(message_proc) { ... }
(lambda
is another way of constructing a Proc
object; the different syntax makes for a bit different semantics, but the point is lambda
would work just as well.)
And a perverse example (obsolete):
require 'continuation'
callcc do |notfound|
...find(notfound) { ... }
...
end
Upvotes: 3