Pages

12/13/2012

Ruby's Symbol-To-Proc

Here is how symbol-To-Proc works, explained in reduction steps.

First, I'll just create a variable 'words' as an array of strings, so we can have something to work with.
words = %w{test hello world}

Starts to use this symbol, '&' = symbol-to-proc
words.map(&:upcase)

'&' is symbol-to-proc character, which means it interprets the following symbol(':upcase') with .to_proc()
words.map(&:upcase.to_proc)

If you look into the source code of Symbol.to_proc ('to_proc' method in 'Symbol' class), you'll find it returns something like this back.
words.map(&proc{|obj,*args| obj.send(:upcase,*args)})

Then, '&' convert the proc into a block (notice the braces).
words.map{|obj,*args| obj.send(:upcase,*args)}

Since, '.map' only provides one argument for a block. The *args is neglected.
words.map{|obj| obj.send(:upcase)}

'.send' is similarly called directly with the symbol.
words.map{|obj| obj.upcase}

The result is
=> ["TEST", "WORLD", "HELLO"]

Now, let's try it with a method with 2 arguments, such as '.inject'

Here are the normal ways that we can write inject with.
(1..10).inject(:+)  #only with Ruby 1.9
(1..10).inject(&:+)
(1..10).inject(0,:+)  #only with Ruby 1.9
(1..10).inject(0,&:+)
(1..10).inject{|result,element| result+element}
(1..10).inject(0){|result,element| result+element}
*whereas 0 is initial value of the summation

Now, the reduction of the '.inject'.

Starting from the basic one.
(1..10).inject(&:+)

with Symbol-to-proc, it interprets this symbol (':+') as it is a proc (or called with .to_proc)
(1..10).inject(&:+.to_proc)

What .to_proc does with a symbol is
(1..10).inject(&proc{|obj,*args| obj.send(:+,*args)})

Then, this proc becomes a block for .inject
(1..10).inject{|obj,*args| obj.send(:+,*args)}

Since '.inject' can have a block that takes 2 arguments, I'll rename the arguments as
(1..10).inject{|result,element| result.send(:+,element)}

So then, '.send' can be substituted with a direct call
(1..10).inject{|result,element| result.+(element)}

Or with out using dot (only for operators)
(1..10).inject{|result,element| result + element}

Finally, the result is
=> 55

** remark: every code line is syntactically correct.

Suggest For Further Watching/Readings:
 - http://www.youtube.com/watch?v=aISNtCAZlMg
 - http://phrogz.net/symbol-to-proc-with-multiple-arguments
 - http://www.potstuck.com/2011/08/06/ruby-symbols-instead-of-blocks/

No comments:

Post a Comment