The Artima Developer Community
Sponsored Link

Ruby Buzz Forum
The trouble with injection

0 replies on 1 page.

Welcome Guest
  Sign In

Go back to the topic listing  Back to Topic List Click to reply to this topic  Reply to this Topic Click to search messages in this forum  Search Forum Click for a threaded view of the topic  Threaded View   
Previous Topic   Next Topic
Flat View: This topic has 0 replies on 1 page
Matt Williams

Posts: 466
Nickname: aetherical
Registered: Feb, 2008

Matt Williams is a jack-of-all trades living in Columbus, OH.
The trouble with injection Posted: Aug 13, 2008 1:48 PM
Reply to this message Reply

This post originated from an RSS feed registered with Ruby Buzz by Matt Williams.
Original Post: The trouble with injection
Feed Title: Ramblings
Feed URL: http://feeds.feedburner.com/matthewkwilliams
Feed Description: Musings of Matt Williams
Latest Ruby Buzz Posts
Latest Ruby Buzz Posts by Matt Williams
Latest Posts From Ramblings

Advertisement

Ruby’s injection is very useful, but if you don’t remember one key fact, you’ll shoot yourself in the foot.

The inject method allows you to perform an operation over all the members of an Enumerable, keeping track of a value throughout.  However, the caveat is that you must return the value at each step.
Suppose we wanted to obtain the sum of the numbers from 1 to 100?

# Gauss would be jealous!!!
>> (1 .. 100).inject() {|sum, n| sum + n}
=> 5050

Ok, that works. But what about the sum of the even numbers?

>> (1 .. 100).inject() {|sum, n| (sum + n) if (n%2) == 0 }
NoMethodError: undefined method `+' for nil:NilClass
	from (irb):38
	from (irb):38:in `inject'
	from (irb):38:in `each'
	from (irb):38:in `inject'
	from (irb):38

That’s not at all what we expected. Here’s why:

If you recall from before, the value needs to be returned at each pass through the block.  However, here if the number is not even, the block returns an implicit nil.  Then things get weird when we attempt to add a number to nil.  Let’s try this again:

>> (1 .. 100).inject() {|sum, n| (n % 2)==0 ? sum + n : sum }
=> 2551

Much better.

Another place where errors occur are with arrays and hashes.  Suppose I wanted (contrived) to collect objects representing the next seven days in an array.  Here’s an attempt (which fails):

>> require 'rubygems'
>> require 'activesupport'
>> (1 .. 7).inject([]){|s, n| s[n] = Time.now + 1.day}
NoMethodError: undefined method `[]=' for Thu Aug 14 15:32:58 -0400 2008:Time
	from (irb):40
	from (irb):40:in `inject'
	from (irb):40:in `each'
	from (irb):40:in `inject'
	from (irb):40

What’s happening can be illustrated below:

>> s=[]
=> []
>> s[1]=1
=> 1
>> s=(s[1]=1)
=> 1
>> s[2]=2
NoMethodError: undefined method `[]=' for 1:Fixnum
	from (irb):45

The assignment returns the value assigned. Since it is not an array, when we attempt to use an index, we get an error. Here’s a version which works:

>> week = (0 ... 7).inject([]) do |s,n|
?>     s.push((Time.now + n.day))
>>   end
=> [Wed Aug 13 15:03:47 -0400 2008, Thu Aug 14 15:03:47 -0400 2008, Fri Aug 15 15:03:47 -0400 2008, Sat Aug 16 15:03:47 -0400 2008, Sun Aug 17 15:03:47 -0400 2008, Mon Aug 18 15:03:47 -0400 2008, Tue Aug 19 15:03:47 -0400 2008]

Note the use of Array#push which returns the entire array. Admittedly, it’s very contrived. A better way would be to use map, but that’s a discussion for another day.

>> (1 .. 7).map do |n| Time.now + n.day;end
=> [Thu Aug 14 15:04:20 -0400 2008, Fri Aug 15 15:04:20 -0400 2008, Sat Aug 16 15:04:20 -0400 2008, Sun Aug 17 15:04:20 -0400 2008, Mon Aug 18 15:04:20 -0400 2008, Tue Aug 19 15:04:20 -0400 2008, Wed Aug 20 15:04:20 -0400 2008]

Read: The trouble with injection

Topic: Hash key rewrite Previous Topic   Next Topic Topic: Ruby/Rails Meetup - New Ruby Patterns - w/ Sunny Hirai - Sept/8 @ The Network Hub

Sponsored Links



Google
  Web Artima.com   

Copyright © 1996-2019 Artima, Inc. All Rights Reserved. - Privacy Policy - Terms of Use