Understanding Computation

Understanding Computation by Tom Stuart Page B

Book: Understanding Computation by Tom Stuart Read Free Book Online
Authors: Tom Stuart
Tags: COMPUTERS / Programming / General
Ads: Link
«
do-nothing
» and leave a modified environment
     behind. We can think of big-step statement evaluation as a process
     that always turns a statement and an initial environment into a final
     environment, avoiding the small-step complication of also having to
     deal with the intermediate statement generated by
#reduce
. Big-step evaluation of an
     assignment statement, for example, should fully evaluate its
     expression and return an updated environment containing the resulting
     value:
class
Assign
def
evaluate
(
environment
)
environment
.
merge
({
name
=>
expression
.
evaluate
(
environment
)
})
end
end
    Similarly,
DoNothing#evaluate
will clearly return the unmodified environment, and
If#evaluate
has a pretty straightforward job
     on its hands: evaluate the condition, then return the environment that
     results from evaluating either the consequence or the
     alternative.
class
DoNothing
def
evaluate
(
environment
)
environment
end
end
class
If
def
evaluate
(
environment
)
case
condition
.
evaluate
(
environment
)
when
Boolean
.
new
(
true
)
consequence
.
evaluate
(
environment
)
when
Boolean
.
new
(
false
)
alternative
.
evaluate
(
environment
)
end
end
end
    The two interesting cases aresequence statements and «
while
» loops. For
     sequences, we just need to evaluate both statements, but the initial environment needs to
     be “threaded through” these two evaluations, so that the result of evaluating the first
     statement becomes the environment in which the second statement is evaluated. This can be
     written in Ruby by using the first evaluation’s result as the argument to the
     second:
class
Sequence
def
evaluate
(
environment
)
second
.
evaluate
(
first
.
evaluate
(
environment
))
end
end
    This threading of the environment is vital to allow earlier
     statements to prepare variables for later ones:
>>
statement
=
Sequence
.
new
(
Assign
.
new
(
:x
,
Add
.
new
(
Number
.
new
(
1
),
Number
.
new
(
1
))),
Assign
.
new
(
:y
,
Add
.
new
(
Variable
.
new
(
:x
),
Number
.
new
(
3
)))
)
=> «x = 1 + 1; y = x + 3»
>>
statement
.
evaluate
({})
=> {:x=>«2», :y=>«5»}
    For «
while
» statements, we need to think through
     the stages of completely evaluating a loop:
    Evaluate the condition to get either «
true
» or
     «
false
».
If the condition evaluates to «
true
», evaluate
     the body to get a new environment, then repeat the loop within that new environment
     (i.e., evaluate the whole «
while
» statement again)
     and return the resulting environment.
If the condition evaluates to «
false
», return
     the environment unchanged.
    This is a recursive explanation of how a «
while
»
     statement should behave. As with sequence statements, it’s important that the updated
     environment generated by the loop body is used for the next iteration; otherwise, the
     condition will never stop being «
true
», and the loop
     will never get a chance to terminate. [ 15 ]
    Once we know how big-step «
while
» semantics should behave, we can
     implement
While#evaluate
:
class
While
def
evaluate
(
environment
)
case
condition
.
evaluate
(
environment
)
when
Boolean
.
new
(
true
)
evaluate
(
body
.
evaluate
(
environment
))
when
Boolean
.
new
(
false
)
environment
end
end
end
    This is where the looping happens:
body.evaluate(environment)
evaluates the
     loop body to get a new environment, then we pass that environment back into the current method to kick off the
     next iteration. This means we might stack up many nested
     invocations of
While#evaluate
until thecondition eventually becomes «
false
» and the final environment is
     returned.
    Warning
    As with any recursive code, there’s a risk that the Ruby call stack will overflow if
     the nested invocations become too deep. Some Ruby implementations have experimental
     support for
tail call optimization
, a technique that reduces the
     risk of overflow by reusing the same stack frame when possible. In the official Ruby
     implementation (MRI) we can enable tail call

Similar Books

Dorothy Garlock

Glorious Dawn

After Dark

Donna Hill

No Greater Love

William Kienzle

Ship of Secrets

Franklin W. Dixon

The Trouble with Mark Hopper

Elissa Brent Weissman