This post was migrated from Justin’s personal blog, 'Codethinked.com.' Views, opinions, and colorful expressions should be taken in context, and do not necessarily represent those of Simple Thread (and were written under the influence of dangerous levels of caffeination).

Click here to view the entire IronRuby via C# series

In my previous post on learning Ruby via Iron Ruby and C# we left off with a little bit about loops in Ruby and how the classic looping constructs are rarely used in Ruby. I showed you that in Ruby you can do a simple “for” loop like this:

for number in 1..10  
    puts number  
end

But in Ruby you are more likely to do it like this (although this would probably still be fairly rare):

(1..10).each do |num|
    puts num
end

And as I mentioned, you can do this in C#:

foreach (int i in Enumerable.Range(1,10)){
    Console.WriteLine(i);
}

Most people don’t know that ranges exist in C#. That is probably because it was just added in .net 3.5. As you can see, it is part of the library. Not a good or bad thing, just something to point out. Ruby ranges are a bit different, and quite a bit more flexible. In Ruby the “1..10” creates a range from 1 to 10, and then “1…10” creates a range from 1 to 9. Very subtle, and I’m sure it has been the source of at least a few bugs. 🙂

One problem with C# ranges is that the first number is the start, and the last number is the count. Not the start and end numbers. The distinction is subtle, but important. In C# if we do this:

foreach (int i in Enumerable.Range(-5,-1)){
    Console.WriteLine(i);
}

We will get an ArgumentOutOfRange exception. What would we get with Ruby if we did this:

(-5..-1).each do |num|
    puts num
end

Well, we would get the numbers -5, -4, -3, -2, and -1 printed out! If we wanted to do this in C#, we would have to do this:

foreach (int i in Enumerable.Range(-5, 5))
{
    Console.WriteLine(i);
}

In my mind (and I’m a C# programmer) the Ruby one makes more sense! They both work, I just think the Ruby one reads easier. Ruby ranges also support a step method that allows us to make ranges that don’t increment by 1:

(1..10).step(2) do |num|
    puts num
end

In C#, we could do this:

foreach (int i in Enumerable.Range(1,10).Where(x => (x + 1) % 2 == 0))
{
    Console.WriteLine(i);
}

We could also just define our own “Step” extension method, which would probably make life easier for us.

Another cool part about Ruby ranges is that you don’t have to use numbers:

("a".."z").each do |char|
    puts char
end

And yes, this prints out the letters from “a” to “z” as a range. What happens when we do this:

("aaa".."bbb").each do |char|
    puts char
end

Yep, we get every permutation from “aaa” to “bbb”, such as aab, aac, aad, aae, etc… All 704 of them. I know there is 704 because I did this:

puts ("aaa".."bbb").to_a.length

Sweet. “to_a” puts the range into an array. Then “.length” just returns the length of that array. Ranges in Ruby really are flexible and powerful, in fact, there is even more that you can do with them that we aren’t even going to cover here. Just to whet your appetite though, Ruby can actually construct objects using ranges. It requires a bit more knowledge that we have covered so far, but you can actually do something like “Person.new(“aaa”)..Person.new(“bbb”)” and construct all of the objects for every permutation.

One thing to note though is that C# ranges are implemented using deferred execution. Therefore, you can get a range from 0 to Int32.MaxValue and if you call “.Take(10)” on it, then the first 10 numbers will be generated. The rest of the list will never be created. Ruby also does this with ranges, which means that you can do things like this:

infinity = 1.0/0        
infinite_range = (1..infinity)                
infinite_range.each do |num|
    if (num > 15)
        break
    end
    puts num            
end

If Ruby was not implementing this in a delayed fashion, then we might get an error from this. 🙂 Ruby has a representation of “infinity” that it uses to store numbers which are too large or small for it to deal with. Here we are getting a reference to infinity by using 1.0/0. We could have also done something like “10**10**10” which is 10 to the power of 10 to the power of 10. Once we have our infinity representation we can use it as the end of the our range. Then we can Range.each passing in each number until we pass 15, in which case we break. Only the numbers 1 to 15 are ever created. This allows us to pass a range into another method and then let that method decide how many items it is going to pull off the list.

In C# we could simply do a range using “Enumerable.Range(1..Int32.MaxValue)” since the Range method is bounded by the length of Int32 anyways. Ruby on the other hand is capable of storing extremely large numbers. And I mean extremely large numbers. If you don’t believe me, do 10000**10000 and look at how many zeros you are going to get (** is the exponential operator)! And yes, that is ten thousand to the power of ten thousand.

Well, it appears that my tour of Ruby ranges went on a bit longer than anticipated. So I am going to end it there for the evening, and next time I am going to finally get into Ruby arrays and hashes. I hope you enjoyed!

Leave a Reply

Your email address will not be published. Required fields are marked *