knotito
knotito

Reputation: 1392

Call of a method as a block

Beginner in ruby world, I would like to do something like:

[1,2.0,"a",2].select(&:is_a?(Integer))

but like this it definitely don't work...

Any ideas?

Upvotes: 2

Views: 125

Answers (2)

Chris Heald
Chris Heald

Reputation: 62648

&:method_name is syntactic sugar for &:method.to_proc. Enumerators like select and whatnot accept a block and yield each element of the enumerator to the passed block. That is:

[1,2,3].select &:even?

is equivalent to:

p = :even.to_proc
[1,2,3].select {|val| p.yield(val) }

Since only the parameters yielded by the enumerator are yielded to the proc, you would have to include them in the source list. That is, we might expect:

[[1, Integer]].select &:is_a?

to result in:

select {|*args|, p.yield(*args) }

However, remember that p isn't a method bound to any particular class! It's going to try to invoke the given method on the passed argument. So, it's going to try to invoke Array#is_a? with no arguments, rather than splatting the arguments out and invoking Integer#is_a?(Integer).

So, to accomplish this, we'll have to somehow create a proc that binds the passed arguments, and then calls the given method on the yielded receiver with the passed args. We can do this by adding a method to the Symbol class:

  class Symbol
    def with_args(*args)
      proc {|receiver| receiver.send(self, *args) }
    end
  end

  [1, "a"].select &:is_a?.with_args(Integer)

While it's perhaps not amazingly clean, it does work.

Upvotes: 1

Alex Peachey
Alex Peachey

Reputation: 4686

You can't do what you are asking for because when you use the & syntax you have to use a method that doesn't take parameters.

However, if you for some reason you really want to do something like that, you need to make a method that doesn't take parameters like so:

class Object
  def is_an_integer?
   is_a? Integer
  end
end

You can then do:

[1,2.0,"a",2].select(&:is_an_integer)

Upvotes: 2

Related Questions