Understanding Computation (29 page)

Read Understanding Computation Online

Authors: Tom Stuart

Tags: #COMPUTERS / Programming / General

BOOK: Understanding Computation
11.55Mb size Format: txt, pdf, ePub
Note

As promised, we’re writing
ONE
instead of
-> p { -> x { p[x] } }
, and so on, to
make the code clearer.

Unfortunately, this program doesn’t work anymore, because we’re now using operations
like
..
and
%
on the
proc-based implementations of numbers. Because Ruby doesn’t know how to treat these as
numbers it’ll just blow up:
TypeError: can't iterate from
Proc
,
NoMethodError: undefined method `%'
for #
, and so on. We need to replace all of the
operations to work with these representations—and we can only use procs to do it.

Before we can reimplement any of the operations, though, we need
implementations of
true
and
false
.

Booleans

How can we
represent Booleans using only procs? Well, Booleans exist solely to be used in
conditional statements, and in general, a conditional says “if some Boolean then
this
else
that
”:

>>
success
=
true
=> true
>>
if
success
then
'happy'
else
'sad'
end
=> "happy"
>>
success
=
false
=> false
>>
if
success
then
'happy'
else
'sad'
end
=> "sad"

So the real job of a Boolean is to allow us to choose between two options, and we can
take advantage of this by representing a Boolean as a proc that chooses one of two values.
Instead of thinking of a Boolean as a lifeless piece of data that can be read by some future
code to decide which of two options to choose, we’ll just implement it directly as a piece
of code that, when called with two options, either chooses the first option or chooses the
second option.

Implemented as methods, then,
#true
and
#false
could be:

def
true
(
x
,
y
)
x
end
def
false
(
x
,
y
)
y
end

#true
is a method that takes
two arguments and returns the first one, and
#false
takes two arguments and returns the
second. This is enough to give us crude conditional behavior:

>>
success
=
:true
=> :true
>>
send
(
success
,
'happy'
,
'sad'
)
=> "happy"
>>
success
=
:false
=> :false
>>
send
(
success
,
'happy'
,
'sad'
)
=> "sad"

As before, it’s straightforward to translate these methods into
procs:

TRUE
=
->
x
{
->
y
{
x
}
}
FALSE
=
->
x
{
->
y
{
y
}
}

And just as we defined
#to_integer
as a sanity check, to make sure it
was possible to convert proc-based numbers into Ruby numbers, so we can
define a
#to_boolean
method that can
turn the
TRUE
and
FALSE
procs into Ruby’s native
true
and
false
objects:

def
to_boolean
(
proc
)
proc
[
true
][
false
]
end

This works by taking a proc that represents a Boolean and calling it with
true
as its first argument and
false
as its second.
TRUE
just returns its
first argument, so
to_boolean(TRUE)
will return
true
, and likewise for
FALSE
:

>>
to_boolean
(
TRUE
)
=> true
>>
to_boolean
(
FALSE
)
=> false

So representing Booleans with procs is surprisingly easy, but for FizzBuzz, we don’t
just need Booleans, we need a proc-only implementation of Ruby’s
if
-
elsif
-
else
. In fact, because of the way these Boolean implementations work, it’s easy
to write an
#if
method too:

def
if
(
proc
,
x
,
y
)
proc
[
x
][
y
]
end

And that’s easy to translate into a proc:

IF
=
->
b
{
->
x
{
->
y
{
b
[
x
][
y
]
}
}
}

Clearly
IF
doesn’t need to do any useful work,
because the Boolean itself picks the right argument—
IF
is
just sugar—but it looks more natural than calling the Boolean directly:

>>
IF
[
TRUE
][
'happy'
][
'sad'
]
=> "happy"
>>
IF
[
FALSE
][
'happy'
][
'sad'
]
=> "sad"

Incidentally, this means we can revise the definition of
#to_boolean
to use
IF
:

def
to_boolean
(
proc
)
IF
[
proc
]
[
true
][
false
]
end

While we’re refactoring, it’s worth noting that the implementation of
IF
can be cleaned up significantly, because it contains some
procs that are equivalent to simpler ones, as discussed in
Equality
.
For example, look at
IF
’s innermost proc:

->
y
{
b
[
x
][
y
]
}

This code means:

  1. Take an argument
    y
    .

  2. Call
    b
    with
    x
    to get a proc.

  3. Call that proc with
    y
    .

Steps 1 and 3 are dead wood: when we call this proc with an
argument, it just passes it on to another proc. So the whole thing is
equivalent to just step 2,
b[x]
, and
we can remove the dead wood in the implementation of
IF
to make it simpler:

IF
=
->
b
{
->
x
{
b
[
x
]
}
}

We can see the same pattern again in what’s now the innermost
proc:

->
x
{
b
[
x
]
}

For the same reason, this proc is the same as just
b
, so we can simplify
IF
even further:

IF
=
->
b
{
b
}

We’re not going to be able to simplify it any more than
that.

Note

IF
doesn’t do anything
useful—it’s
TRUE
and
FALSE
that do all the work—so we
could
simplify further by getting rid of it
altogether. But our goal is to translate the original FizzBuzz
solution into procs as faithfully as possible, so it’s convenient to
use
IF
to remind us where the
if
-
elsif
-
else
expression appeared in the original,
even though it’s purely decorative.

Anyway, now that we have
IF
, we
can go back to the FizzBuzz program and replace
the Ruby
if
-
elsif
-
else
with nested calls to
IF
:

(
ONE
.
.
HUNDRED
)
.
map
do
|
n
|
IF
[
(
n
%
FIFTEEN
)
.
zero?
][
'FizzBuzz'
][
IF
[
(
n
%
THREE
)
.
zero?
][
'Fizz'
][
IF
[
(
n
%
FIVE
)
.
zero?
][
'Buzz'
][
n
.
to_s
]]]
end
Predicates

Our next job is to
replace
Fixnum#zero?
with a proc-based implementation that will work with proc-based numbers.
The underlying algorithm of
#zero?
for Ruby values is something like this:

def
zero?
(
n
)
if
n
==
0
true
else
false
end
end

(This is more verbose than is necessary, but it’s explicit about what happens: compare
the number with
0
; if it’s equal, then return
true
; otherwise, return
false
.)

How can we adapt this to handle procs instead of Ruby numbers?
Look at our implementation of numbers again:

ZERO
=
->
p
{
->
x
{
x
}
}
ONE
=
->
p
{
->
x
{
p
[
x
]
}
}
TWO
=
->
p
{
->
x
{
p
[
p
[
x
]]
}
}
THREE
=
->
p
{
->
x
{
p
[
p
[
p
[
x
]]]
}
}

Notice that
ZERO
is the only number that doesn’t call
p
—it just returns
x
—whereas all of the other numbers call
p
at
least once. We can take advantage of this: if we call an unknown number with
TRUE
as its second argument, it’ll return
TRUE
immediately if the number is
ZERO
. If it’s not
ZERO
, then it’ll return
whatever calling
p
returns, so if we make
p
a proc that always returns
FALSE
, we’ll get the behavior we want:

def
zero?
(
proc
)
proc
[->
x
{
FALSE
}
][
TRUE
]
end

Again, it’s easy to rewrite this as a proc:

IS_ZERO
=
->
n
{
n
[->
x
{
FALSE
}
][
TRUE
]
}

We can use
#to_boolean
on the
console to check that it works:

>>
to_boolean
(
IS_ZERO
[
ZERO
]
)
=> true
>>
to_boolean
(
IS_ZERO
[
THREE
]
)
=> false

That’s working fine, so in FizzBuzz, we can replace all of the calls to
#zero?
with
IS_ZERO
:

(
ONE
.
.
HUNDRED
)
.
map
do
|
n
|
IF
[
IS_ZERO
[
n
%
FIFTEEN
]
][
'FizzBuzz'
][
IF
[
IS_ZERO
[
n
%
THREE
]
][
'Fizz'
][
IF
[
IS_ZERO
[
n
%
FIVE
]
][
'Buzz'
][
n
.
to_s
]]]
end

Other books

Passionate Bid by Tierney O'Malley
Mark of the Witch by Maggie Shayne
A Bride for Donnigan by Janette Oke
If You Wrong Us by Dawn Klehr
One by One by Chris Carter
Riesgo calculado by Katherine Neville
Gates of Hell by Susan Sizemore