magpiebrain

Sam Newman's site, a Consultant at ThoughtWorks

Following on from Part 2, we look at two of Ruby’s most important collection classes, Hashes and Arrays, as well as have a brief look at the typing system.

Update 1: Fixed typo, thanks Anjan – any day now I’ll stop writing this stuff so late at night I’m too tired to proof read.

Update 2: Fixed blatant syntax error – thanks Chris

Arrays

We’ve already seen a little of one of Ruby’s built in types, the array. You can work with them much like Java’s Array:

[ruby]
array = [4, 6.0, “Hello”]
array0 —> 4
array1 —> 6.0
array2 —> “Hello”
array.length—> 3
[/ruby]

Where in Java we’d need a for loop to iteratie through the elements in an array, in Ruby we can use a number of built-in array method each. For example to print out the string value of each item in the array:

[ruby]
array.each { | item | puts item.to_s }
[/ruby]

The array class makes use of the Enumerable module – any class which implements each and @ can include the @Enumerable module, getting a variety of helpful methods for free:

[ruby]
array = [4, 6, 8, 10]
array.find_all { | item | item => 8 } —> [8, 10]
array.include?(6) —> true
array.include?(“Fish”) —> false
array.partition { | item | item [[4, 6], [8, 10]]
array.min —> 4
array.collect { | item | item – 2} —> [2, 4, 6, 8]
[/ruby]

Hashes

Another of Ruby’s collection types is Hash. Like Java’s Hashtable or HashMap classes – a construct which uses a key to index an object. To access the values in a hash, you use the element reference construct, []:

[ruby]
ages = { “sam” => 28, “norman” => 60, “fred” => 45 }
ages[“sam”] —> 28
ages[“fred”] —> 45
ages[“jane”] —> nil
ages[“sam”] = 30
ages[“sam”] —> 30
[/ruby]

Hash also implements Enumerable:

[ruby]
ages = { “sam” => 28, “norman” => 60, “fred” => 45 }
ages.each { | name, age | puts ”#{name} is #{age} years old”}
ages.sort —> [[“fred”, 45], [“norman”, 60], [“sam”, 28]]
ages.include?(“sam”) —> true
ages.include?(“jane”)—> false
ages.partition { | name, age | age [ [[“fred”, 45], [“sam”, 28]], [[“norman”, 60]] ]
[/ruby]

We’ll be looking at more advanced uses of both Arrays and Hashes later on.

“Quack quack” – the Ruby typing system

There is a saying – “If it walks like a duck and talks like a duck, it must be a duck”. In Ruby, you don’t say something is a duck – you make it act like a duck. In Java, you hang a nice big sign around it’s neck saying “duck”, then go about implementing the duck methods. On the face of it, this isn’t a big difference. Compare:

public interface Duck {
  void waddle(int distance);

  String speak();
}

	

public class Mallard implements Duck {

public void waddle(int distance) { ... } public void speak() { return "Quack!"; } }

public class Pintail implements Duck {

public void waddle(int distance) { ... } public String speak() { return "Quack!"; } }

And in Ruby:

class Mallard
  def waddle(distance)
    ...
  end

  def speak
    return "Quack!"
  end
end

	

class Pintail

def waddle(distance) ... end def speak return "Quack!" end end

Now lets imagine a Dog class, who can also speak:

public interface Dog {
  String speak();
}

	

class Dalmation implements Dog {

public String dog() { return "Woof!"; } }

And in Ruby:

class Dog
  def speak
    puts "Woof!"
  end
end

Now lets imagine a Naturalist, who’s job it is to study the sights and more importantly the sounds of the wildlife around him. If we just wanted to study ducks, then we could define a simple interface:

public interface Naturalist {
  void study(Duck duck);
}

	

public class WildlifeReporter implements Naturalist {

public void study(Duck duck) { tapeRecorder.record(duck.speak()); } }

And in Ruby:

class Naturalist
  def study(duck)
    tape_recorder.record(duck.speak)
  end
end

Now if we wanted our Naturalist to also study a Dog, in Java we’d either have to define two methods – one study that takes a Duck, and another that takes a Dog – but more likely we’d define another interface (say, Animal) from which both Dog and Duck derive, and redefine study to take that new interface. With Ruby, our Naturalist is the same for both – all it cares about is that whatever gets passed into study defines a speak method.

Now implementing the Animal doesn’t seem like a big deal, and in this (highly contrived) example, it isn’t. But you can see how you’ll quickly end up with a number of type definitions, all defining the same operations, just so you can work in a generic fashion with a variety of objects. The typical solution to this in Java is to have many very fine-grained interfaces (in some cases you’ll even see many interfaces only implementing a single method).

We’ll be looking more at the Ruby typing system in later parts.

12 Responses to “Ruby For Java (and C#) Programmers, Part 3 – Introducing Arrays, Hashes and the typing system”

  1. Anjan Bacchu

    Nice piece. Keep it going. I’m going through dave’s ruby book(2nd ed) and it’ll be nice how this continues.

    TYPO
    ====
    “…..stufy the sights and more importantly the sounds of the wildlife around him…..”

    you meant “study” the sights…. ?

    BR,
    ~A

    Reply
  2. Sam Newman

    Aslak – I do plan to look at API comparisons (especially IO and XML handling) although this might be in part 5. In the next part I’m either going to look at the dynamic nature of Ruby’s typing, or look at more advanced use of Hashes/Arrays and some string handling.

    Oh, and thanks for stopping the typo Anjan – I really shouls stop writing this stuff late at night…

    Reply
  3. Gary Blomquist

    If you’re taking votes, I vote for “the dynamic nature of Ruby’s typing” for the next article.

    Reply
  4. sam newman

    That’s certainly coming up Gary – although next I think I’m either going to look at string and IO handling, or I’ll be looking at map/reduce.

    Reply
  5. Josh Graham

    How do Java’s new “Generics” (like C++ Templates) solve (or not) your comparison on the Duck / Dog speak method call. I’m reckoning they do.

    Reply
  6. Sam Newman

    I’m not sure how they do solve the above example (and there are differences between Java generics and C++ templates) – the initial problem here is that @Dog@ and @Dog@ are in seperate hierarchies. The two Java solutions are either to implement a more fine-grained interface (e.g. @Communicative@) which defines the @speak@ method, or to define a new base interface as I showed above. With Ruby there is no need for either. Don’t forget as well that Ruby’s type system is also dynamic – the lack of a dynamic typing system is the reason why you end up having to mess around with code generation, bytecode weaving and dynamic proxies for things like AOP, JDO and Hibernate in Java.

    Reply
  7. Chris Rimmer

    Um, shouldn’t this:

    array.find_all { | item | item => 8 } –> [8, 10]

    be this?

    array.find_all { | item | item >= 8 } –> [8, 10]

    Reply
  8. sam newman

    Yes Chirs, it certainly should 🙂 In an alternative universe, I’m sure there is a me who actually proof reads this stuff and runs the code snippets through irb…

    Reply
  9. Chris Rimmer

    Call me Mr Picky if you want (it’s an improvement on “Chirs” at least), but I also think you mean “Naturalist”.

    Reply
  10. Jules

    Nice article, except:

    @array.each { | item | puts item.to_s }@

    Because:

    @array.each { | item | puts item }@

    and:

    @puts array@

    Would do *exactly* the same thing. Maybe:

    @array.each{|item| puts item.length}@

    But that could be:

    @puts array.map{|item| item.length}@

    In Ruby world, you’d write something as simple as possible, and that means as concise as possible most times.

    And it’s often a good idea to separate the IO from the “real” code.

    Really good article though. I’d like to read more about Enumerable methods, because this slightly functional module is hard to understand for Java/C# programmers. (C# is going functional too, with anonymous functions)

    Keep up the good work!

    Reply

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

Basic HTML is allowed. Your email address will not be published.

Subscribe to this comment feed via RSS

%d bloggers like this: