Some students have struggled somewhat with tracing procedure calls. This handout is intended to provide an additional example to help those striving to get things right.
We’ll start with a few reminders.
let
bindings, and more. We’ll leave those for other examples.Let’s consider an example, similar to one has appeared on various learning assessments.
We’ll start with some definitions.
(define a "A")
(define b "B")
(define f
(lambda (x)
(string-append x "-" x)))
(define g
(lambda (y)
(string-append "(" y ")")))
(define p
(lambda (x y)
(g (string-append (f y)
"/"
(g x)))))
And we need something to evaluate. Here goes.
(p (f a) (g b))
Where do we start? Well, before we can apply p
, we must evaluate both arguments. We’ll start with the (f a)
. Before we can apply f
, we must evaluate a
. Our definitions tell us that a
is "A"
, so we subtitute.
--> (p (f "A") (g b))
Can we evaluate that "A"
any further? Nope. So we plug "A"
in for x
in the body of f
, giving us (string-append "A" "-" "A")
. We replace (f "A")
with that new expression.
--> (p (string-append "A" "-" "A") (g b))
All three arguments to string-append
are fully evaluated. Hence, we can append the three strings.
--> (p "A-A" (g b))
We’ve gone as far as we can with the first argument to p
, so we move on to the second argument. Before we call g
, we must evaluate b
. Our definitions tell us that b
is "B"
.
--> (p "A-A" (g "B"))
Now, we need to substitute "B"
in for y
in the body of g
. That gives us (string-append "(" "B" ")")
. We replace (g "B")
with that new expression.
--> (p "A-A" (string-append "(" "B" ")"))
The three parameters to string-append
are now all evaluated, so we’re able to append them.
--> (p "A-A" "(B)")
Both parameters to p
are now fully evaluated. We can then substitute "A-A"
for x
in the body of p
and substitute "(B)"
for y
.
--> (g (string-append (f "(B)") "/" (g "A-A")))
We can’t yet evaluate the outer g
until we evaluate the string-append
. We can’t evaluate the string-append
until we evaluate the calls to f
and g
.
Fortunately, the argument to f
is already evaluated. So we substitute "(B)"
for x
in the body of f
.
--> (g (string-append (string-append "(B)" "-" "(B)") "/" (g "A-A")))
All the parameters to the inner string-append
are now evaluated, so we can just append them.
--> (g (string-append "(B)-(B)" "/" (g "A-A")))
The first argument to the outer-string append is now complete. The second argument (the "/"
) is also complete. We move on to the inner call to g
. We substitute "A-A"
for y
in the body of g
and then plug the result in for the call to g
.
--> (g (string-append "(B)-(B)" "/" (string-append "(" "A-A" ")")))
All the arguments to the inner string-append
are complete. We can apply that procedure.
--> (g (string-append "(B)-(B)" "/" "(A-A)"))
We’re getting close. The outer call to string-append
can now be evaluated.
--> (g "(B)-(B)/(A-A)")
We now substitute "(B)-(B)/(A-A)"
for y
in g
.
--> (string-append "(" "(B)-(B)/(A-A)" ")")
Finally, we append those strings.
--> "((B)-(B)/(A-A))"
And we’re done.
Putting it all together, we get the following.
(p (f a) (g b))
--> (p (f "A") (g b))
--> (p (string-append "A" "-" "A") (g b))
--> (p "A-A" (g b))
--> (p "A-A" (g "B"))
--> (p "A-A" (string-append "(" "B" ")"))
--> (p "A-A" "(B)")
--> (g (string-append (f "(B)") "/" (g "A-A")))
--> (g (string-append (string-append "(B)" "-" "(B)") "/" (g "A-A")))
--> (g (string-append "(B)-(B)" "/" (g "A-A")))
--> (g (string-append "(B)-(B)" "/" (string-append "(" "A-A" ")")))
--> (g (string-append "(B)-(B)" "/" "(A-A)"))
--> (g "(B)-(B)/(A-A)")
--> (string-append "(" "(B)-(B)/(A-A)" ")")
--> "((B)-(B)/(A-A))"
Now, you may say to yourself “I could tell that g
surrounds its parameter with parentheses and that f
makes two copies and separates them with a dash. And you’d be right. However, for tracing (at least for LAs), our goal is that you can work out the steps, not just that you get the right answer for these procedures. Why? Because tracing is important when you can’t figure out what a procedure does. And if you don’t master the skill of tracing straightforward procedures, you won’t be able to transfer more subtle ones