Alex Rivera | Logout

Shorthand for functions to map

Asked 2012-02-24T20:39:01.277
9

In a map, I can call a method on the passed-in value using the convenient &: notation:

nums = (0..10).to_a
strs = nums.map(&:to_s)

Is there something similar for calling a function with the value passed in as the first argument?

nums = (0..10).to_a
nums.each(puts) # error!
Edit
Report

1 Answer

11

Disclaimer: This post is purely educational. nums.each {|n| puts n} is really the only reasonable thing to write in a real project.

Understanding nums.map(&:to_s)

The existing short form works very simply. & calls to_proc on the symbol, and to_proc on a symbol is defined like this.

class Symbol
  def to_proc
    Proc.new { |*args| args.shift.__send__(self, *args) }
  end
end

Since this proc will start acting like a regular block that is passed into map, the *args in this case is really each element we're iterating through. We take the first of args (since * turns arguments into an array), and send self to it, self being the actual symbol, such as :to_s. Remaining arguments are passed in. So it's like saying nums.map{ |*args| args.shift.__send__(:to_s, *args) }.

Changing it to enable nums.each(&:puts)

We could easily re-implement to_proc to act differently. Here's a quick example.

class Symbol
  def to_proc
    Proc.new { |*args| __send__(self, *args) }
  end
end

(1..10).each(&:print) # => 12345678910

Here instead of sending symbol name as a message to the element, we are just calling symbol as a method on the current context, and simply passing the iterated element as an argument to it.

So it's more like saying (1..10).each{|*args| __send__(:print, *args)}.

Understanding nums.each(&method(:puts))

That said, as nash pointed out you could call nums.each(&method(:puts)). What happens there is that you get an object that represents method puts using ruby's method method. So then & calls .to_proc on the method object, turning it into proc, which itself s

answered 2012-02-24T21:04:10.890

Your Answer