Programming Languages: Fall 2005
From SubfireWiki
Contents |
Question 1: haskell
Here's a (perhaps overly complex) solution:
(\l -> map (\i -> snd i) (filter (\i -> fst i) (zip (foldl (\a b -> a ++ [b == 7]) [] l) (drop 1 l) )))
> follow7::[Float] -> [Float] > follow7 [] = [] > follow7 [a] = [] > follow7 (a:b:x) > | a == 7.0 = b:follow7 (b:x) > | otherwise = follow7 (b:x)
Question 2: prolog
permute([ ], [ ]).
permute(L, [X | Y] ):- select(L, X, T),
permute(T, Y).
select([X | Y], X, Y).
select([X | Y], Z, [X | T]):- select(Y, Z, T).
permute([1,2,3], X) will follow a depth-first search tree which builds a new list by selecting the first element (1), and permuting the rest of the list [2,3]. The recursive call to permute([2,3], X) then selects the first element (2) and permutes the rest of the list [3], which ends up just being [3]. The rest of the list is effectively patched together putting together the (1), (2), and (3) above, resulting in [1,2,3]
If a semicolon is pressed, then the prolog engine backtracks up to the last select call that had a "choice", which selects (3) instead of (2), which results in a final list of [1,3,2].
Next time a semicolon is pressed, the engine backtracks up one more level to match (2) as the first element, which will end up resuling in a final list of [2,1,3]
Question 3 : semantics
(a) Denotational semantics
- Branching (if statements)
- We are adding a statement like {"if" E "then" L1 "else" L2} in our language. Goal is to explain precisely what it does "denotationally".
- Write this:
S[[���?if���?� E ���?then���?� L1 ���?else���?� L2]](Env) =
if E[[E]](Env)>0 then L[[L1]](Env)
else L[[L2]](Env)
- then explain what this means
- we're defining a new statement,
- a statement takes an environment and gives back a new environment (for the rest of the statements to use).
- The if statement will evaluate the expression in the current environment
- if the expression 'E' evaluates to greater than zero, execute the first list of statements (which will consequently modify the environment in one way)
- if the expression evaluates to less or equal to zero, evaluate the second list of statements with the environment given, which will modify the env in a different way.
- then explain what this means
(resulting in a new enviornment that is the composition of the modifications of each statement in the list)
- Looping (while statements)
- We are adding a statement like {"while" E "do" L}
- Wriet this:
S[[ ���?while���?� E ���?do���?� L]](Env) =
if E[[E]](Env)<0 then Env else
S[[ ���?while���?� E ���?do���?� L]](L[[L]](Env))
- then explain what it means
- First evaluate E in the given environment
- If it's less than zero, return the current environment
- Otherwise get the new environment by evaluating the statement list in the given environment. Pass that to a new do/while instance. (thus checking the expression in the new environment ...)
- First evaluate E in the given environment
- then explain what it means
(b) Axiomatic semantics
This is wrong:
- Actually, you need to start with the P, work backwards through the code (as we have done), make sure that
can derive what we find after all the substitutions. As a second step, you must also prove that
derives the end result: x = = gdc(x0,y0)
x0 is initialize value of x, y0 is initial value of y
precondition/loop invariant =
postcondition = preconditions
Taking the whole post condition:
x=temp
y=x%y
int temp=y
Given the post conditions, we have found that the loop invariant and pre conditions hold. Because we find that y = = 0 and we know that
which
therefore, x = gcd(x0,y0)
note: gcd(x,y%x) = gcd(x,y)
Question 4: Reference model vs. value model
- Differences
- Stack allocation vs. heap allocation
- dynamic dispatching vs. static
- Similarities
- Because value parameters are passed by reference, the objects are not copied in either case, just the reference/pointer is passed.
- templates:
- add generic types/code without dynamic dispatching, but it's only at compile time
- in the value model, objects are copied on the stack so functions are dispatched statically. You need to pass the object by reference to get functions to be dispatched dynamically.
Question 5: non-determinism
- compiler writer can focus on fairness
- Programmer can focus on service
- unpredictable
- execution can be overlapped
