In my previous post, I went through the Day 1 Ruby problems from Seven Languages in Seven Weeks. Today, I’ll share my solutions to the Day 2 problems and some more thoughts about Ruby.

Ruby, Day 2: Thoughts

I originally learned Ruby (and many other programming languages) the “hacker way”: that is, I did a 10 minute syntax tutorial, browsed other peoples’ code a bit, and then just started using the language, looking up missing pieces as I went. Although this is the most fun and productive way I’ve found to get started with a language, it can also lead to missing some of the finer points and subtleties.

For example, until the “Ruby, Day 2” chapter, I never had a full appreciation for Ruby code blocks and the yield keyword. For example, even though I frequently used “times” to do looping, I never thought deeply about how it worked:

10.times { puts "Jim" }

It turns out that times is just a function (slightly obscured because Ruby doesn’t require parentheses for function calls) on the Integer class that takes a code block as an argument. It could be implemented as follows:

class Integer
  def times
    1.upto(self) { yield }
  end
end

This style of coding allows for some powerful possibilities. For example, it is surprisingly easy to introduce a “do in a transaction” function:

def within_a_transaction
  start_transaction
  yield
  end_transaction
end

Using this, I can now trivially wrap any number of statements in a transaction:

within_a_transaction { do_something }
 
within_a_transaction do
  do_something
  do_something_else
  do_a_third_thing
end

The equivalent in less expressive languages, such as Java, often involves vastly more code, implementing arbitrary interfaces, anonymous inner classes, and a lot of very hard-to-read code. For comparison, here is an example of how Java’s Spring Framework recommends wrapping JDBC code in transactions:

return _transactionManager.execute(new VoidDBExecCallback() {
  public void doExecute(DBExecContext dbExecContext) throws TransactionException {
    dbExecContext.getJdbcTemplate().execute(
      "Select name from tbl_foo where id = ?", 
      new PreparedStatementSetter() {
        public void setValues(PreparedStatement ps) throws SQLException
        {
          ps.setInt(1, 12345);
        }
      }, 
      new RowCallbackHandler() {
        public void processRow(ResultSet rs) throws SQLException
        {
          String name = rs.getString(1);
        }
      }
    );
  }
});

Ruby, Day 2: Problems

The Day 2 problems are only slightly tougher than Day 1. The most fun part was coming up with a way to keep the code as concise as possible.

Print the contents of an Array of 16 numbers, 4 numbers at a time, using just each. Now, do the same with each_slice in Enumerable.

arr = (1..16).to_a
arr.each { |i| print "#{i}#{i % 4 == 0 ? "\n" : ','}" }
arr = (1..16).to_a
arr.each_slice(4) { |slice| puts slice.join(", ") }

Tree

Modify the Tree class initializer (original code here) so it can accept a nested structure of Hashes. Trickiest part here was that the collect function can call the passed in block with either one argument that’s an Array or two arguments that represent the (key, value) pair.

class Tree
  attr_accessor :children, :node_name
  
  def initialize(tree = {})
    @node_name = tree.keys[0]
    @children = tree[@node_name].collect{ |k, v| Tree.new({k => v}) }
  end
  
  def visit_all(&block)
    visit &block
    children.each { |c| c.visit_all &block }
  end
  
  def visit(&block)
    block.call self
  end
end
 
tree = Tree.new({"grandpa" => {"dad" => {"child1" => {}, "child2" => {}}, "uncle" => {"child3" => {}, "child4" => {}}}})
tree.visit_all { |node| puts node.node_name }

Grep

Write a simple grep that will print the lines and line numbers of a file having any occurrence of a phrase anywhere in that line.

# Usage: ruby grep.rb <regular_expression> <file_name>

IO.readlines(ARGV[1]).each_with_index{ |line, index| puts "#{index + 1}: #{line}" if line =~ /#{ARGV[0]}/}

Ruby vs. Java, Round 2

I couldn’t resist implementing the grep code in Java to see how it compares:

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.regex.Pattern;
import java.util.regex.Matcher;
 
/**
 * Usage: java Grep <regular_expression> <file_name>
 */
public class Grep {
  public static void main(String[] args) throws IOException {
    Pattern pattern = Pattern.compile(args[0]);
    BufferedReader br = null;
    String line = null;
    int lineNumber = 1;
    
    try {
      br = new BufferedReader(new FileReader(new File(args[1])));
      while((line = br.readLine()) != null) {
        Matcher matcher = pattern.matcher(line);
        if (matcher.find()) {
          System.out.println(lineNumber + ": " + line);
        }
        lineNumber++;
      }    
    } finally {
      if (br != null) {
        br.close();
      }
    }
  }
}

It’s 33 lines long. The Ruby solution was a one-liner.

Ruby, Continued

Check out more Ruby goodness on Ruby, Day 3.