[SUMMARY] Hello, world? (#158)

M

Matthew Moss

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 metho=
d,
but with Enumerable's inject method and String's concatenation operator, wh=
ich
will convert an argument between 0 and 255 to a character before concatenat=
ion.

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 t=
he
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=
 
J

James Gray

There are even a few solutions I still haven't figured out yet. I =20
need to break down the dense code from _why to get a handle on =20
what's going on.

I assume we're talking about this:

require 'rbconfig'
bui =3D /^bui(.{2})$/
$stdout << "#{{}.class}"[0,1] <<
("#{{}.methods}"[/c(\w{4})c/] && $1.reverse) <<
(([0]*2).inspect[2,2]) <<
Config::CONFIG.keys.grep(bui).first.gsub(bui,
"#{Kernel.methods.grep(/^th/)[0][2,3].reverse}\\1") <<
ObjectSpace._id2ref(338)

Yeah, that's crazy. Let's see if we can figure it out.

require 'rbconfig'

Require Ruby's configuration details. That gives us a bunch of fresh =20=

Strings to work with.

bui =3D /^bui(.{2})$/

Define a regular expression that matches words like "build."

$stdout << "#{{}.class}"[0,1] <<

=46rom here on out, it's all output. This creates a Hash, and pulls =20=

the first character off of that class name.

("#{{}.methods}"[/c(\w{4})c/] && $1.reverse) <<

Now it starts to get tricky. This code pulls the methods for a Hash, =20=

joins them into a giant String, uses a Regexp to find "collec" from =20
the collect() method, and reverses and prints the "olle" portion. =20
That gets us the rest of "Hello."

(([0]*2).inspect[2,2]) <<

This makes an Array, grabs the code for it from Ruby's inspect(), and =20=

indexes into that String to pull out the comma and space.

Config::CONFIG.keys.grep(bui).first.gsub(bui,
"#{Kernel.methods.grep(/^th/)[0][2,3].reverse}\\1") <<

This uses the Regexp built a while back to match the "build" key out =20
of Ruby's configuration Hash. It then replaces the letters "bui." To =20=

get the replacement, the code hunts for the throw() method on Kernel, =20=

pulls the last three letters and reverses them. That gives us "wor" + =20=

"ld" or "world."

ObjectSpace._id2ref(338)

I have no idea how reliable this is, but in _why's build of Ruby, and =20=

my own, the object with the ID 338 is the Symbol :"!", the last =20
character needed.

Clever code as always from _why.
And I'm somewhat frightened to even contemplate the mind of Rub=E9n =20=
Medell=EDn, whose solution is some bizarre, palindromic mirror image =20=
of Ruby insanity.

It was freaking awesome. I'll leave it to someone else too spoil that =20=

one=85 ;)

James Edward Gray II
 
K

Ken Bloom

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.

I assume we're talking about this:

require 'rbconfig'
bui = /^bui(.{2})$/
$stdout << "#{{}.class}"[0,1] <<
("#{{}.methods}"[/c(\w{4})c/] && $1.reverse) <<
(([0]*2).inspect[2,2]) <<
Config::CONFIG.keys.grep(bui).first.gsub(bui,
"#{Kernel.methods.grep(/^th/)[0][2,3].reverse}\\1") <<
ObjectSpace._id2ref(338)

Yeah, that's crazy. Let's see if we can figure it out.

require 'rbconfig'

Require Ruby's configuration details. That gives us a bunch of fresh
Strings to work with.

bui = /^bui(.{2})$/

Define a regular expression that matches words like "build."

$stdout << "#{{}.class}"[0,1] <<

From here on out, it's all output. This creates a Hash, and pulls
the first character off of that class name.

("#{{}.methods}"[/c(\w{4})c/] && $1.reverse) <<

Now it starts to get tricky. This code pulls the methods for a Hash,
joins them into a giant String, uses a Regexp to find "collec" from the
collect() method, and reverses and prints the "olle" portion. That gets
us the rest of "Hello."

(([0]*2).inspect[2,2]) <<

This makes an Array, grabs the code for it from Ruby's inspect(), and
indexes into that String to pull out the comma and space.

Config::CONFIG.keys.grep(bui).first.gsub(bui,
"#{Kernel.methods.grep(/^th/)[0][2,3].reverse}\\1") <<

This uses the Regexp built a while back to match the "build" key out of
Ruby's configuration Hash. It then replaces the letters "bui." To get
the replacement, the code hunts for the throw() method on Kernel, pulls
the last three letters and reverses them. That gives us "wor" + "ld" or
"world."

ObjectSpace._id2ref(338)

I have no idea how reliable this is, but in _why's build of Ruby, and my
own, the object with the ID 338 is the Symbol :"!", the last character
needed.

Clever code as always from _why.
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.

It was freaking awesome. I'll leave it to someone else too spoil that
one… ;)

James Edward Gray II

def method_missing(a = p, *c); return ; nruter ;(c* p = a);gnissim_dohtem fed
end ; dne

These first two lines define method_missing (in Object context) to do nothing. The code returns immediately (at return),
so none of the addional reversed names are executed. The dne after the end of method_missing calls method_missing, since no
method named dne is defined. We can now delete most mangled names that we don't understand. The equivalent code for this
is:

def method_missing (a=p, *c); return; end

The p is never evaluated (it would point to nowhere, or cause infinite recursion), because method_missing
is always called with the name of an argument

alias m method_missing ; gnissim_dohtem m; saila



class NilClass ; ssalCliN ssalc
alias inspect to_s ; s_ot tcepsni ;saila
end ; dne

NilClass.inspect now returns an empty string. Everything to the right of the semicolons is a call to method_missing.
This can be safely removed since it's never called anyway.

class Integer ; regetnI ssalc
def method_missing(a=chr,*b);print chr;return a.to_s[0] ; [0];s_ot.a nruter;rhc tnirp;(b*rhc=a);gnissim_dohtem fed
end ; dne

Supposing we call 63.method_missing directly. Then a will be ascii character 63 ('?'), and the function will print '?' and
return 63. Supposing we call 72.e. Then a will be the letter 'e'. The function will print ascii character 72 ('h') and return
the ascii code for 'e' (101). We can now call (for example) puts 72.e.l.l.o.chr and get the output 'Hello'


def d! ; return(d.e and puts) ; (stup dna; e.d);nruter ; !d fed
dne ; end ; dne ; end

Still operating on the integer context, this prints the previous letter, then a 'd' (through two missing methods)
and uses puts to place a blank line after the d.
The 2 end calls close the method p, and close the Integer class.


def p(p = a, *b) ; begin ; nigeb ; (b* a = p); p fed
rescue ; print p.to_s ; return ; nruter ; s_ot.p tnirp ; eucser
dne ; end ; dne ; end

Sorting out some of the method_missing calls:

def p(p=nil, *b)
begin
a=p #evalutes p as a variable
p fed #evaluates p as a function, causing a SystemStackError
rescue #catches the SystemStackError
#this is caught at the innermost level of the stack overflow
print p.to_s #evaluates p as a variable, which at the inner level is nil, so this prints nothing
return
end
end

The whole function definition can be safely removed, as called, it does the same thing as method_missing anyway.

dne def a b = c ; return nil ; lin nruter ; c = b a; fed end

Defines a method a which returns nil. dne calls method_missing with a nil argument. Since "def" to define a method is an
expression that returns nil, its return value can be passed to a function. This line can be safely removed since it
defines a method that does the same thing as method_missing.

a = a def fed a = a
return nruter
a = p end ; fed ; def dne p = a
p a ; a p
end ; dne

He breaks palindrome with a single semicolon here.

Sets the variable a to nil, (because the second a is a method call), and defines the method fed to return
the return value of nruter (which at this moment is a method_missing call, and stays that way for the rest of the program)
The line
a = p end
is acutally two statements: an a=p, followed by an end.
He then goes to immediately define dne to call the p function we saw before, which prints nothing, and
the a function which returns nil. So dne is now defined, but does nothing.

These 5 lines can actually all be safely removed, since they define methods which do the same thing as method_missing anyway.


def h ax0=0xa;return 0xa unless ax0;dne;y=x p;fed = def p x=y;end; 0xa;sselnu ax0;nruter ax0=0xa;h fed
bx0, dx0 = 0xd, 0xb
bx0 + dx0 + 0xb + bx0 + 0xd + 0xb
end ; dne

def h ax0=10
return 10 unless ax0
y=nil
fed = nil
def p x=y
end
bx0=13
dx0=11
bx0 + dx0 + 11 + bx0 + 13 + 11 # equals 13 + 11 + 11 + 13 + 13 + 11 which is 72
end

In short, if nil is explicitly passed to h, it returns 10. If anything else (or nothing) is passed to h,
it defines p (in the global context) to do nothing, then returns 72 (ascii for 'H').

def Object.const_missing a ; a gnissim_tsnoc.tcejbO; fed
return send(a.to_s.downcase) ; (esacnwod.s_ot.a);dnes nruter
end ; dne

All the stuff on the right side of the semicolons does nothing. The const_missing is intended to
convert the H constant reference on the next line to a call to the h method we just defined. (This will
return the ascii code for 'H', which is what we need to start the sequence of Integer.method_missing calls
on the next line.

H.e.l.l.o._.w.o.r.l.d! ; !d.l.r.o.w._.o.l.l.e.H

The left side prints out the string "Hello_world" using the integer method_missing chaining that I mentioned above. (The last
call to d! prints out the letter "d", as mentioned above.)
The right side calls the global method_missing repeatedly, which does nothing.

In other words, all of this code actually translates to:
(Everything else is calls method_missing which returns nil,
or calls a method which is functionally equivalent to method_missing.)

class Integer
def method_missing a, *b
print chr
return a.to_s[0]
end
def d!
return (d.e and puts)
end
end

def h
return 72
end

def Object.const_missing a
return send(a.to_s.downcase)
end

H.e.l.l.o._.w.o.r.l.d!
 
A

Arlen Cuss

[Note: parts of this message were removed to make it a legal post.]

Hi,

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?

I think, as far as my own experience goes, that many people - despite
perhaps being grammar sticklers - still are used to seeing "Hello, World!"
from many test programs while learning to code. Just the way things turned
out, maybe. :)

Arlen
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,982
Messages
2,570,190
Members
46,736
Latest member
zacharyharris

Latest Threads

Top