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 D.:
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 B. presented
a solution that looked like a maze, but simply extracted the letters
from
the ASCII maze and joined them appropriately. (Unlike Bill K.'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(‘[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 M. 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 W.:
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. A few
probabilistic
solutions, including Bill K.'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 Q. 2 will return next
Friday.