Nathaniel Miller
Nathaniel Miller

Reputation: 167

Ruby's find method - argument?

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

Answers (2)

philip yoo
philip yoo

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

Amadan
Amadan

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

Related Questions