Gray Soft

The programming blog of James Edward Gray II (JEG2).
  • 21

    MAR
    2006

    The Why and How of Iterators

    A friend of mine has been asking some general questions about iterators in private emails we have traded. I wanted to put some of my answers here, in case they appeal to a wider audience.

    Why do we have iterators?

    First, let's invent a little data to play with:

    >> Name = Struct.new(:first, :last)
    => Name
    >> names = [ Name.new("James", "Gray"),
    ?>           Name.new("Dana", "Gray"),
    ?>           Name.new("Caleb", "Nordloh"),
    ?>           Name.new("Tina", "Nordloh") ]
    => [#<struct Name first="James", last="Gray">,
        #<struct Name first="Dana", last="Gray">,
        #<struct Name first="Caleb", last="Nordloh">,
        #<struct Name first="Tina", last="Nordloh">]
    

    Now let's assume we want to print some names. We can use the each() iterator for that, no index:

    >> names.each { |name| puts "#{name.last}, #{name.first}" }
    Gray, James
    Gray, Dana
    Nordloh, Caleb
    Nordloh, Tina
    => [#<struct Name first="James", last="Gray">,
        #<struct Name first="Dana", last="Gray">,
        #<struct Name first="Caleb", last="Nordloh">,
        #<struct Name first="Tina", last="Nordloh">]
    

    Read more…

  • 10

    MAR
    2006

    Java a Bit on the Wordy Side

    I was helping a friend of mine with a Java problem yesterday and couldn't help but notice this totally normal (for Java) file in his project:

    import java.io.Serializable;
    
    public class Contact implements Serializable
    {
       private String firstName;
       private String lastName;
       private String email;
       private String phone;
    
       public Contact()
       {
           this("", "", "", ""); // call four-argument constructor
       } // end no-argument Contact constructor
    
       // initialize a record
       public Contact(String first, String last, String eml, String phn)
       {
           setFirstName(first);
           setLastName(last);
           setEmail(eml);
           setPhone(phn);
       } // end four-argument Contact constructor
    
       // set first name
       public void setFirstName(String first)
       {
          firstName = first;
       } // end method setFirstName
    
       // get first name
       public String getFirstName()
       {
          return firstName;
       } // end method getFirstName
    
       // set last name
       public void setLastName(String last)
       {
          lastName = last;
       } // end method setLastName
    
       // get last name
       public String getLastName()
       {
          return lastName;
       } // end method getLastName
    
       // set email address
       public void setEmail(String eml)
       {
          email = eml;
       } // end method setEmail
    
       // get email address
       public String getEmail()
       {
          return email;
       } // end method getEmail
    
       // set phone number
       public void setPhone(String phn)
       {
          phone = phn;
       } // end method setPhone
    
       // get phone
       public String getPhone()
       {
          return phone;
       } // end method getPhone
    } // end class Contacts
    

    Read more…

  • 22

    FEB
    2006

    Class Level Mix-ins

    A question that comes up pretty often in Ruby circles was posed again today, by Xavier Noria:

    #
    # Is there standard idiom to add module methods to classes that mix them in?
    #
    # That is, I would like class C to croak:
    #
    
    module M
      def self.croak
        puts "Croak!"
      end
    end
    
    class C
      include M
      croak  # doesn't work
    end
    

    This brings up a couple of interesting points about mix-ins. Obviously, class methods are not easily added to things, but we generally want class methods so we can call them directly from the module. Here's how I deal with this issue.

    First, instance methods are the way to go for anything you will be mixing in, period. With instance methods, you can inject them into a class or object. With anything else you have to start hacking. That gives us our first change:

    module M
      def croak
        puts "Croak!"
      end
    end
    

    Now, we really wanted a module method, so I've made things worse. However, as I've said, we have all the choices with this setup. Let's just mix the module into it's own class!

    Read more…

  • 22

    FEB
    2006

    Currying

    All the examples in this chapter are trivially translated (switch sub { ... } to lambda { ... }). Ironically, I have never seen a chunk of idiomatic Ruby do anything like this. Rubyists clearly favor blocks for this sort of work. Have a look at the stream addition and multiplication examples of this chapter, for example. You can also see this when MJD trying to create a suitable inject() for Perl (he calls it reduce/fold).

    Another interesting point about this chapter is how much of it is spent warring with Perl's syntax. MJD really struggles to introduce a block-like syntax for curried methods and is outright defeated in a couple of attempts. I really like how easily Ruby yields to our attempts to reprogram her, in sharp contrast to her sister language.

    Continuing that line of thought, here's my best effort at the Poor Man's Currying library:

    #!/usr/bin/env ruby -w
    
    class Proc
      def curry(&args_munger)
        lambda { |*args| call(*args_munger[args]) }
      end
    end
    
    class Object
      def curry(new_name, old_name, &args_munger)
        ([Class, Module].include?(self.class) ? self : self.class).class_eval do
          define_method(new_name) { |*args| send(old_name, *args_munger[args]) }
        end
      end
    end
    

    Read more…

  • 20

    FEB
    2006

    Infinite Streams

    I've tried to summarize this chapter a couple of times now, but I keep getting tripped up over syntax. So, let's talk about that…

    Functional Perl

    Obviously, the examples in the book can be more or less directly translated. Here's a sample from the first couple of pages:

    #!/usr/local/bin/ruby -w
    
    ### Stream Methods ###
    
    def node(head, tail)
      [head, tail]
    end
    
    def head(stream)
      stream.first
    end
    
    def tail(stream)
      tail = stream.last
      if tail.is_a?(Proc)
        tail.call
      else
        tail
      end
    end
    
    def drop(stream)
      head                 = head(stream)
      stream[0], stream[1] = Array(tail(stream))
      head
    end
    
    def show(stream, limit = nil)
      while head(stream) && (limit.nil? or (limit -= 1) > -1)
        print drop(stream), $, || " "
      end
      print $/
    end
    
    ### Examples ###
    
    def upto(from, to)
      return if from > to
      node(from, lambda { upto(from + 1, to) })
    end
    show(upto(3, 6))  # => 3 4 5 6
    
    def upfrom(start)
      node(start, lambda { upfrom(start + 1) })
    end
    show(upfrom(7), 10)  # => 7 8 9 10 11 12 13 14 15 16
    

    Read more…

  • 31

    JAN
    2006

    Iterators (Chapters 4 and 5)

    Due to a printing error, these two chapters actually came out longer than intended. Originally their contents were: "Use Ruby."

    All jokes aside, there's really not a whole lot for me to talk about from these chapters, since iterators are so internal to Ruby. Readers from our camp should run into a lot less surprises here that the intended audience. Just translate MDJ's anonymous subroutines to blocks, replace his returns with yields, and you are 90% of the way there.

    Here are translations for some of the examples in these chapters. I think these all come out cleaner and more natural in Ruby, but you be the judge:

    Permutations

    #!/usr/local/bin/ruby -w
    
    def permute(items)
      0.upto(1.0/0.0) do |count|
        pattern = count_to_pattern(count, items.size) or break
        puts "Pattern #{pattern.join(' ')}:" if $DEBUG
        yield(pattern_to_permutation(pattern, items.dup))
      end
    end
    
    def pattern_to_permutation(pattern, items)
      pattern.inject(Array.new) { |results, i| results + items.slice!(i, 1) }
    end
    
    def count_to_pattern(count, item_count)
      pattern = (1..item_count).inject(Array.new) do |pat, i|
        pat.unshift(count % i)
        count /= i
        pat
      end
      count.zero? ? pattern : nil
    end
    
    if ARGV.empty?
      abort "Usage:  #{File.basename($PROGRAM_NAME)} LIST_OF_ITEMS"
    end
    
    permute(ARGV) { |perm| puts(($DEBUG ? "  " : "") + perm.join(" ")) }
    

    Read more…

  • 20

    JAN
    2006

    Caching and Memoization

    I felt this chapter had a lot going for it, in places, but occasionally got lost in the details. All in all though, it's good stuff.

    Caching

    Obviously a powerful technique here and all of it translates to Ruby with little effort. Here's a direct translation of the RGB_to_CMYK() subroutine:

    #!/usr/local/bin/ruby -w
    
    $cache = Hash.new
    
    def rgb_to_cmyk(*rgb)
      return $cache[rgb] if $cache.include?(rgb)
      c, m, y     = rgb.map { |color| 255 - color }
      k           = [c, m, y].min
      $cache[rgb] = [c, m, y].map { |color| color - k } + [k]
    end
    
    unless ARGV.size == 3 && ARGV.all? { |n| n =~ /\A\d+\Z/ }
      abort "Usage:  #{File.basename($PROGRAM_NAME)} RED GREEN BLUE"
    end
    
    puts rgb_to_cmyk(*ARGV.map { |num| num.to_i }).join(", ")
    

    There are several interesting syntax differences in there. For example, I had to use a global variable for the $cache because Ruby methods don't have access to local variables. Another option would be to use a lambda(). These are probably good indicators that we would wrap this in an object and use instance variables normally.

    Read more…

  • 17

    JAN
    2006

    Dispatch Tables

    I think in Ruby we tend to do a lot of this kind of work with method_missing(). I told you, Functional OO Programming.

    Here's my attempt at something close to a direct translation of the RPN calculator example:

    #!/usr/local/bin/ruby -w
    
    $stack = Array.new
    
    def rpn(expression, operations_table)
      tokens = expression.split(" ")
      tokens.each do |token|
        type = token =~ /\A\d+\Z/ ? :number : nil
    
        operations_table[type || token][token]
      end
    
      $stack.pop
    end
    
    if ARGV.size == 2 && ARGV.first == "-i" && ARGV.last =~ /\A[-+*\/0-9 ]+\Z/
      require "pp"
    
      def ast_to_infix(ast)
        if ast.is_a?(Array)
          op, left, right = ast
          "(#{ast_to_infix(left)} #{op} #{ast_to_infix(right)})"
        else
          ast.to_s
        end
      end
    
      ast_table = Hash.new do |table, token|
        lambda { |op| s = $stack.pop; $stack << [op, $stack.pop, s] }
      end.merge(:number => lambda { |num| $stack << num.to_i })
    
      puts "AST:"
      pp(ast = rpn(ARGV.last, ast_table))
      puts "Infix:"
      pp ast_to_infix(ast)
    elsif ARGV.size == 1 && ARGV.first =~ /\A[-+*\/0-9 ]+\Z/
      calculation_table = Hash.new do |table, token|
        raise "Unknown token:  #{token}."
        end.merge(
          :number => lambda { |num| $stack << num.to_i },
          "+"     => lambda { $stack << $stack.pop + $stack.pop },
          "-"     => lambda { s = $stack.pop; $stack << $stack.pop - s },
          "*"     => lambda { $stack << $stack.pop * $stack.pop },
          "/"     => lambda { d = $stack.pop; $stack << $stack.pop / d }
        )
    
        puts rpn(ARGV.first, calculation_table)
    else
      puts "Usage:  #{File.basename($PROGRAM_NAME)} [-i] RPN_EXPRESSION"
    end
    

    Read more…

  • 17

    JAN
    2006

    Recursion and Callbacks

    I'm currently reading through Higher-Order Perl, by Mark Jason Dominus. (Yes, I read books about things other than Ruby.)

    So far, I'm enjoying the title quite a bit. It certainly has me thinking and the Perl in it is very clean and easy to understand. That helps me translate the concepts to my language of interest.

    I'll post some of my Ruby translations of the books example code here as I go along. Others familiar with the book might enjoy looking over them. Be warned, my comments might not make much sense to those who haven't read the book.

    Recursion

    The book starts with some very simple recursion examples trivially translated. Here's one for manually translating Integers to binary Strings (a long way to say str.to_i.to_s(2) in Ruby):

    #!/usr/local/bin/ruby -w
    
    def binary(number)
      return number.to_s if [0, 1].include?(number)
    
      k, b = number.divmod(2)
      binary(k) + b.to_s
    end
    
    unless !ARGV.empty? && ARGV.all? { |n| n =~ /\A\d+\Z/ }
      abort "Usage:  #{File.basename($PROGRAM_NAME)} DECIMAL_NUMBERS"
    end
    
    puts ARGV.map { |num| binary(num.to_i) }.join(" ")
    

    Read more…

  • 15

    JAN
    2006

    What Not to Test

    I've now seen multiple claims amounting to something like, "I built this Rails application with just testing." I think it's great that people can do that. They are obviously smarter than me. I need a browser to build a Web application.

    I do test my Rails projects, of course. I'm a huge fan of testing and I'm always telling people how much it could help with their work. However, I believe there's a time to test and a time not to, if you can imagine that. Yes, you heard me right, there are things I don't test and I'm sure this next revelation will shock you even more:

    I don't test Rails views.

    Now before everyone fires up their mail client and pours on the hate mail, let's make sure we are very clear about what I just said. I do write thorough unit tests for all of my model classes, of course. I also test the controllers as much as possible. I make sure the data I expect ends up in the right instance variables with the magic assigns(). I also use the session Hash to validate what I am remembering about the user. If a controller action is a complex logic branch that can end up in several different places, I will also make sure I add some assertions to verify that the right template handled the response.

    Read more…