Beyond Java - Bruce Tate [55]
class Range {
public static void main (String args[ ]) {
int i = 4;
if (2 < i && i < 8) System.out.println("true");
}
}
You can do something similar in Ruby, but you've got another alternative. Ruby supports first-class range support. x..y represents values from x to y, inclusive. For example, 1..3 represents 1, 2, 3. You can include the 3 with a third period. As you can imagine, ranges in Ruby are objects:
irb(main):004:0> range=1..3
=> 1..3
irb(main):005:0> range.class
=> Range
You can also check to see if something is in a range, using the = = = operator:
irb(main):010:0> ('a'..'z') = = = 'h'
=> true
irb(main):011:0> ('a'..'z') = = = 'H'
=> false
irb(main):012:0> (1..10) = = = 5
=> true
You get more convenient syntactic sugar. Now, a for loop turns into this:
irb(main):021:0> for c in 'g'..'k'
irb(main):022:1> puts c
irb(main):023:1> end
g
h
i
j
k
for/in loops also work with Arrays and Hashes. Ranges introduce = = =, another type of comparison. Next, you'll see a third type of comparison, called match, which you'll use with regular expressions .
Regular Expressions
Java has an API that supports regular expressions. Ruby builds regular expressions into the syntax. Some like regular expressions and others do not. To me, they're a critical part of dealing with strings. Just like any other type of programming, you can take them too far. If you've got 16 consecutive backslashes, it's probably time to refactor. Still, they can be much more useful than similar code, handwritten to recognize certain patterns.
In Ruby, you'll define a regular expression between slashes. You'll match regular expressions like this:
irb(main):027:0> regex = /better/
=> /better/
irb(main):028:0> regex.class
=> Regexp
irb(main):029:0> "Mine is bigger" =~ regex
=> nil
irb(main):030:0> "Mine is better" =~ regex
=> 8
Ruby returns the index of the character at the match. Ruby regular expressions are much more powerful than I can show you here. I'll just say that Java developers spend at least half of their time dealing with strings. When you think about it, servlets, XML strings, configuration files, deployment descriptors, and application data can all be strings. To parse them effectively, you need first-class pattern matching, such as regular expressions and ranges. Java 1.5 closes the gap some, but not completely.
Containers
Ruby containers are like Java's collections. You just saw an array. Like Java, arrays are objects: [1,2,3].class returns Array. Unlike Java, everything in an array is also an object. Ruby also has a Hash. Like Java's HashMaps, a Ruby Hash is an object. Unlike Java's HashMap, a Ruby Hash also has some syntactic sugar. You use braces instead of brackets, and you use key =>value to define one key-value pair, like this:
irb(main):011:0> numbers={0=>"zero", 1=>"one", 2=>"two", 3=>"three"}
=> {0=>"zero", 1=>"one", 2=>"two", 3=>"three"}
irb(main):012:0> 4.times {|i| puts numbers[i]}
zero
one
two
three
Like Java collections, Ruby containers hold objects, and they need not be homogeneous. In version 1.5, Java's generics let you build type-safe collections. You could modify Ruby's Array or Hash to make them type safe. (Remember, you can modify any of Ruby's classes directly. It's a dynamic language.) While Ruby doesn't have dozens of types of containers like Java does, you will notice some benefits immediately:
Since there's no distinction between primitives and other objects, you can put literally anything into any given container, and you can nest them easily.
Since everything inherits from object, everything has a hash code.
The language gives you the same syntactic sugar for hashes as for arrays.
Code blocks make iteration tighter and easier.
If you're a big Java collections user who's used a dynamic language before, you probably noticed that Java collections often feel wrong. You have to circumvent static type checking, because you're adding something to a collection as an object, and you're forced to cast it to something