| CS 330 | Home | Schedule | Resources |
Estimated time: 3 hours
The objective of this homework is to understand how a program executes, in particular using tail call optimization.
fun {Sum1 N}
if N==0 then 0
else N+{Sum1 N-1} end
end
fun {Sum2 N S}
if N==0 then S else {Sum2 N-1 N+S} end
end
Now do the following: [As with problem 5 later on: you are welcome to use notational shortcuts, such as showing substitutions instead of environments, using infix + and - operators, and sketching the execution by showing the state of the stack only at key points.
fun {SMerge Xs Ys}
case Xs#Ys
of nil#Ys then Ys
[] Xs#nil then Xs
[](X|Xr)#(Y|Yr) then
if X=< Y then X|{SMerge Xr Ys}
else Y|{SMerge Xs Yr} end
end
end
Expand SMerge into the kernel syntax. Note that X#Yis a tuple of two arguments that can also be written '#'(X Y). The resulting procedure should be tail recursive if the rules of section 2.6.2 are followed correctly.
fun {IsEven X}
if X==0 then true else {IsOdd X-1} end
end
fun {IsOdd X}
if X==0 then false else {IsEven X-1} end
end
We say that these functions are mutually recursive since each function calls the other. Mutual recursion can be generalized to any number of functions. A set of functions is mutually recursive if they can be put in a sequence such that each function calls the next and the last calls the first. For this exercise, show that the calls {IsOdd N} and {IsEven N} execute with constant stack size for all non-negative N. In general, if each function in a mutually recursive set has just one function call in its body, and this function call is a last call, then all functions in the set will execute with their stack size bounded by a constant.
X=[a Z] Y=[W b] X=Y. Show that the variables X, Y, Z and W are bound to the same values, no matter in which order the three unifications are done. In chapter 4 we will see that this order-independence is important for declarative concurrency.
[Note: the book, as well as these problems, occasionally uses infix operators in kernel syntax, for instance, "T=(X>=Y)" on p. 69. The semantics for any binary operator Z=X+Y are as follows:
Substitute any binary operator for "+". -Max]
1 or 2? To familiarize yourself with operational semantics,
manually execute the following small program by detailing all the steps.
Recall that an instruction is executed in one environment (i.e. a mapping of identifiers to variables), and explain why the second {Browse X} displays the value 1.
local X in
X=1
local X in
X=2
{Browse X}
end
{Browse X}
end
Firing squad. Using the formal semantics of the language,
execute the two following programs. Pay attention to the order of the instructions
in each case!
% Program 1 |
% Program 2 |
What precisely is the final state of each program? What do they each display? What are the variables created by each? What are the values of these variables when the programs finish?
2 times 3... Consider the program below:
localTranslate this program into the kernel language, then execute it while following the semantic rules. For the translation into the kernel language, do not forget that functions are procedures, and that the calculation of sub-expressions requires the introduction of variables.
X=2
fun {XTimesY Y}
X*Y
end
in
{Browse {XTimesY 3}}
end
Recursion with construction of a list. The function Copy
is defined below. This function returns a `` copy '' of the list passed
in argument. It goes without saying the function does not have true utility,
we make use of it to study its recursive structure.
fun {Copy Xs}
case Xs of X|Xr then X|{Copy Xr} else nil end
end
Now let us consider two possible translations of this definition of
function in the kernel language. In translation 1 on the left, the recursive
call { Copy Xr } is carried out before building the list with
X. Translation 2 on the right is an alternative. The
single difference is the reversed order of the instructions { Copy
Xr Yr } and Ys=X | Yr. Explain why these
two translations both execute without any problem.
% Translation 1 |
% Translation 2 |
Minimum of a list. Consider the
program below, which defines a function MinLoop. The call {MinLoop
X Ys} return the minimum of the elements of the list X|Ys.
localTranslate this program into the kernel language, then execute it while following the rules of semantics. For the translation into the kernel language, do not forget that functions are procedures, and that the calculation of sub-expressions requires the introduction of variables.
fun {MinLoop X Ys}
case Ys of Y|Yr then
if Y<X then {MinLoop Y Yr} else {MinLoop X Yr} end
else X end
end
in
{Browse {MinLoop 7 [5 8 3]}}
end
With the chain... the function Concat defined
below takes in argument a list of lists Ls and returns the
concatenation of all these lists. One can see Concat as a
generalization of Append for a variable number of lists.
fun {Concat Ls}
case Ls of L|Lr then
{Append L {Concat Lr}}
else nil end
end
This definition, which has the advantage of simplicity, has a weakness
however when one calls it with a large list.
Highlight this weakness using an example, and explain the reason for this weakness.
Bearing in mind the kernel language and the definition of the semantic stack, propose an alternative of Concat not having the problem mentioned.