4 ms·
The way to write this in ruby without extending the symbol class is (1..100).inject { |acc, n| acc + n } inject is a left fold or a reduce- it applies the f
by gregwebs 19y ago
The way to write this in ruby without extending the symbol class is
(1..100).inject { |acc, n| acc + n }
inject is a left fold or a reduce- it applies the function {acc + n} to each element in a list (or array) where acc is the result of the last function application.
For such a simple function, all we need is the knowledge that we are adding, which is contained in the symbol '+', representing the addition method.
So Ideally we would like to write
(1..100).inject( :+ )
The semicolon is how you tell ruby you are using a symbol.
Yes, you can write a simple sum function, but the point is that this code is more flexible in a very powerful way- we can use other functions then inject and add.
Method calling in Ruby is actually message passing. So instead of calling
1.+ 2 # => 3
You can say
1.send( :+, 2 ) # => 3
So we are not trying to convert a symbol to a proc. What we are trying to do is continually send the message ':+' to inject's built in accumulator, with every member of the list (or array) as an argument.
In ruby, the brackets are like a function, but it is actually a closure of class Proc. You can convert a different class to a proc (if it supports it) by calling to_proc.
Ruby has a syntactic shortcut, '&' for converting to a proc. Normally it actually makes the code clearer because it is also used when passing Procs onto other methods, and is a nice type annotation. So we can hack this syntactic shortcut to create
(1..100).inject( &:+ )
So by defining how '&' works by defining to_proc, we can make it create a closure (or Proc) which sends the ':+' symbol, along with any arguments that are yielded to the Proc. In this case, every value from (2..100) is yielded to the Proc. (1 will be the starting value of the accumulator.)
You can accomplish a lot of things with a closure, including this hack. But to me this shows that ruby was not designed as a functional programming language, and how by using Ruby's dangerous flexibility you can manage to warp it towards your needs.
After learning more languages, Ruby is starting to become pretty ugly to me, but somehow people keep managing to ignore all its faults, make little hacks like these, and be very productive with it.
- raganwald 19y ago"After learning more languages, Ruby is starting to become pretty ugly to me, but somehow people keep managing to ignore all its faults, make little hacks like these, and be very productive with it." When I first saw it, I wondered why anyone would want this irregular little language when they could just use Smalltalk or Scheme. An early rant asked why I have to know the difference between a block and a Proc: why the &^(%*)(& do I have to know when to use a block, when to use a Proc, and how to convert between them???
- mdemare 19y agoFor those who think the above is pretty tame and have been using it for years, we've got a series on our blog where we introduce and discuss a new method every day. http://blog.rubyenrails.nl/articles/category/daily_method http://blog.rubyenrails.nl/articles/category/daily_method Here we create a to_proc method for Hash: http://blog.rubyenrails.nl/articles/2008/02/26/a-hash-is-a-function-hash-to_proc http://blog.rubyenrails.nl/articles/2008/02/26/a-hash-is-a-f...
- raganwald 19y agoI subscribe to the series and enjoy it. I hadn't seen Hash#to_proc, I look forward to considering the possibilities carefully...
- gregwebs 19y agobloggified now: http://thoughtfolder.com/2008-02-25-a-detailed-explanation-of-ruby-s-symbol-to-proc.html http://thoughtfolder.com/2008-02-25-a-detailed-explanation-o...
- gregwebs 19y agoactually, brackets represent a block. A block is converted to class Proc with '&'. If the object that '&' is used on is not a block, Ruby will first attempt to convert it into a Proc by calling to_proc A Ruby block is always associated with a method, and cannot be captured in a variable without converting it back into a proc. This is done by 'removing' the '&' def return_block &block return block end
- Readmore 19y agoThe reason I choose to continue with Ruby instead of moving to a language like Scheme has a lot to do with the community. There is so much activity in the Ruby and Rails communities that you can almost always find help with what you're trying to do, a library to make something easier, and great blogs with lots of little tricks. I just haven't seen that same level of community with languages like Scheme. I'm not saying that you should choose your language based solely on the size of it's community (otherwise I would be pulling my hair out with Java) but the advanced features of Ruby, the expressiveness, and the community together make it a great place to be as a programmer.