158 Hello, world?

Description

The first program any new programmer typically sees is one that prints out “Hello, world!” to the console. This tends to be something experienced programmers also see when learning a new language. The first Hello World program was written in B by Kernighan and looked like this:

main( ) {
  extrn a, b, c;
  putchar(a); putchar(b); putchar(c); putchar('!*n');
}
    
a 'hell';
b 'o, w';
c 'orld';

Most programmers are probably more familiar with the typical C implementation:

main() {
  printf("Hello, world!\n");
}

Ruby can present the same output very simply:

puts "Hello, world!"

But that’s too simple… I mean, really… anyone can print a simple string to standard output. Can’t we get more interesting? Hmmm, how about:

puts sprintf("%s, %s!", "Hello", "world")

Eh, that looks too much like C. Maybe…

puts %w(Hello world).join(", ") + "!"

Yeah, that’s definitely looking Ruby-ish.

Your task this week is to print “Hello, world!” to standard output using Ruby in atypical fashion. Some guidelines:


Summary

A simple problem deserves a simple answer… except when you explicitly ask for something other than the simple answer. Which I did ask. And I got answers… lots of them, with a wide variety of techniques, though a few of the methods were repeated, each with slight variations.

First, we have the joining letters technique. Array’s join method makes it easy to create a string from parts, and a number of solutions used this or string concatenation to build up the “Hello, world!” string. Often the primary difference in these solutions was from where the individual letters were taken.

Here is one example of the joining letters technique, from Robert Dober:

puts [?H, ?e, ?l, ?l, ?o, ?,, ?\s, ?W, ?o, ?r, ?l, ?d, ?!].
  inject("") { |s, char| s << char }

Remember that a ? in front of a character returns the ASCII value of that character. Robert joins these values from his array not with the join method, but with Enumerable’s inject method and String’s concatenation operator, which will convert an argument between 0 and 255 to a character before concatenation.

Second, there were a lot of applications of the method missing technique. Usually this involved taking the name of the call and using it as part of the output. There were a number of solutions that looked similar to Jesse Merriman’s first-in method_missing solution:

class Hello
  def method_missing m; print m; self; end
end

Hello.new.H.e.l.l.o.send(', ').w.o.r.l.d!.send("\n")

Since the Hello class doesn’t define any methods except method_missing, any attempt to call a method (except, of course, those defined by Object) will end up in this method_missing call with the argument m containing the attempted method name, which then is immediately printed to standard output. For the few characters that can’t be used as identifiers, the send call defined on Object accomplishes the same trick.

Third, a few people tried the grep technique; that is, extracting the string from the midst of other text or data. Gaspard Bucher presented a solution that looked like a maze, but simply extracted the letters from the ASCII maze and joined them appropriately. (Unlike Bill Kelly’s actual maze solver, which gathers up the letters of the target string in the process of solving the maze.)

A couple folks got “lazy”, so to speak, and decided that rather than generate the “Hello, world!” string on their own, they would get it from somewhere else. Here is elof’s solution for this variant of the grep technique, web scraping:

require 'net/http'
require 'uri'

gke_url = URI.parse("http://www.google.com")
gke = Net::HTTP.start(gke_url.host, gke_url.port) { |http|
  http.get("/search?q=ruby%20quiz%20158")
}
puts gke.body.
  match('\[<b>QUIZ<\/b>\]\s(\w+, \w+).+as often as you can(\W)')[1..2].
  join("")

elof uses standard Ruby net libraries to perform a search on Google and grab the text from the first match. At the time, that first match was suitable to use with the regular expression he employs to pull out the words “Hello world” from the text. While I picked this solution to show off the interaction with the Net::HTTP class, this solution has a good chance of breaking if Google’s page ranking changes for the supplied search string.

Fourth, there were a few self referencing techniques, those solutions that referred to themselves in one way or another to get the answer. Some relied on the __FILE__ constant, requiring that the filename of the Ruby script was named “Hello, world!”. Then there was an interesting solution from Jesse Merriman that loaded its own source code into a variable, and then accessed individual characters from the source via 2-dimensional coordinates. Take a look at Jesse’s solution to see how he got the capital H in there.

To me, the most interesting of these self referencing techniques was the very simple solution from Jari Williamsson:

puts DATA.read
__END__
Hello, World!

This was a new technique that I had not seen before. The __END__ token essentially breaks the file into two parts: code (before) and data (after). The data can be accessed from code by way of the DATA identifier, which is a global IO object. Calling read on that object gets everything after the __END__ token, which Jari immediately dumps to standard out. Very simple, very nice.

Even after all of these techniques, there were still some more things going on. Number conversions where the string was extracted from a large base-256 number… Use of Array’s pack method to convert binary data back into strings… Meta-programming techniques to generate code and methods… Make sure to look at Joel VanderWerf’s solution that, with a few tricks, makes the original B language solution from Kernighan work from within Ruby. A few probabilistic solutions, including Bill Kelly’s gladiator arena.

There are even a few solutions I still haven’t figured out yet. I need to break down the dense code from why** to get a handle on what’s going on. And I’m somewhat frightened to even contemplate the mind of Rub�n Medell�n whose solution is some bizarre, palindromic mirror image of Ruby insanity.

There were a lot of creative solutions going on for this quiz. I highly recommend you peruse them, if not for technique, at least for entertainment. But I do believe most folks will learn some technique here; I certainly did.

My final comment on this quiz… It’s interesting to note that few people actually printed output to spec. The quiz asked for “Hello, world!” while I’d say the majority provided “Hello, World!”. Had I written a unit test to verify the solutions, most would have failed. Granted, I’m getting a little silly in mentioning this, but it made me wonder about two things.

First, why was everyone capitalizing both words? Was “Hello, World!” the more traditional response than “Hello, world!”? Or, perhaps, by having both words capitalized, did it allow some folks to keep the code simpler?

Second, if so few actually nailed the specification exactly, what happens when the specification is much deeper, more involved, more complex? How often do we as developers make minor changes or decisions about non-explicit details rather than confirming the desired behavior with client or customer?

Yeah, I realize this is just a silly “Hello, world” program, and I wasn’t about to hold people to such a strict specification… I just wanted to point out the thinking this inspired in my mind.

Thanks to all who participated in this week’s quiz. There will be no quiz this week, as I will be out of town, hunting for a new apartment in preparation for a move coming at the end of this month. Ruby Quiz 2 will return next Friday.


Wednesday, February 04, 2009