home—lectures—recipe—exams—hws—D2L—breeze (snow day; distance)
hw08
R4 shadowing, and function-application; prolog
Due Dec.02 (Fri) 15:00.
Submit all files on D2L, plus hardcopy of any (parts of) files you added for R3/R4.
Your prolog queries can be inside a comment of a racket file, at the top of
your submitted hardcopy, thanks!
We continue to build on
the language implementation started in hw07.
You can implement this homework in either Java or Racket.
Please indicate in your submitted file, what sections of code are udpated
and what is unchanged from hw07/hw07-soln.
You don't need to turn in any hardcopy of unchanged-code
(but submit a fully-working copy in the drop-box).
Please put new tests into the list “tests”
in the Ri-expr-test file, as possible/appropriate.
R3 is just like R2, except we now
allow one variable to shadow another.
For example, we might have a :o x … :U …
which in turn contains another :o x … :U …
inside of it.
In that case the inner x should shadow the outer one:
:o x 3 :U :o x 5 :U |x 3 ;)| ⇒
:o x 5 :U |x 3 ;)|
⇒
|5 3 ;)| ⇒
8.
And of course,
shadowing may occur between non-adjacent scopes:
:o x 3 :U <:o y 4 :U <:o x 5 :U …>.
In technical terms: when substituting,
only substitute “free occurrences” of an Id
in E1,
not any “bound occurrences”.
Change the purpose-statement of subst
to be “substitute any free occurrences of …”.
(7pts)
Fill in the following blanks:
-
:o y 3 :U :o x 5 :U |x y ;)|
⇒
⇒
⇒
8
-
:o y 3 :U :o x y :U |x y ;)|
⇒
⇒
⇒
6
-
:o x 5 :U :o y 3 :U | :o x y :U |x y ;)| x ;)|
⇒
⇒
⇒
⇒
⇒
11
Hint:
Each step of your answer should remove/simplify the outermost (root) expression
— don't simplify from the inside out.
Re-indent
the following two R3 expressions
so that each :o statement has its three parts equally-indented, on different lines:
Your answer should be 2n+1 lines long, where n is the number of :o
expressions you have.
Q:
In each case, what does the expression evaluate to?
-
:o x 5 :U <:o x |x 1 ;)| :U |x 2 ;)|>
-
:o y :o z 4 :U <:o y 99 :U z> :U <:o z 5 :U |<:o z 10 :U y> |y z ;)| ;)|>
Put your fill-in-blanks in comments next to your test-cases
(with the last two indented as requested).
Also, turn all five of the above into runnable test-cases:
not just for eval (where the last blank is the expected result),
but also for subst (where the first blank is the expected result for a
corresponding call to subst).
You might find it helpful to try to explain (in English) to a friend,
exactly when you do and don't substitute.
-
(5pts)
Update R2 to R3,
by the necessary changes to enable shadowing.
You are encouraged to build on your own previous solution,
but you may also use the
R2 solution
(
overview;
R2.rkt (D2L),
R2-expr-test.rkt,
or email me if interested in a Java solution).
The change should be quite small, but is surgically precise.
Label each section of lines you change with a comment “;>>>R3”.
Note that the change is similar to the difference between
change-blue-to-brown vs. change-blue-to-brown-stopping-at-green,
in AncTrees.
R4 adds (non-recursive) functions and function-application to our language:
Expr ::= … | FuncExpr | FuncApplyExpr
FuncExpr ::= :B Id -> Expr
FuncApplyExpr ::= ! Expr Expr ! |
Be sure not to confuse functions with function-application
(calling a function) — it’s the difference between
square-root (as a function), and the square-root-function-applied-to-4
(or put differently:
it's the difference between a hammer, and hitting something with a hammer).
Here is an example of a function in R4:
:B x -> /x |x 1 ;( x 0.5 xD| ||x 3 xD| 1 ;)|\
|
Just like numbers are self-evaluating,
so are FuncExprs.
If evaluating (an internal representation of) a
FuncExpr,
just return that same (internal representation of the) function.
We won't actually evaluate the body until
the function is applied.
(This is exactly how racket, python, javascript, etc. treat lambda values.)
A FuncApplyExpr represents calling a function.
Here are two expressions, both evaluating to 5·3+1 = 16:
:o tio :B x -> / x |x 1 ;(| ||x 3 xD| 1 ;)|\
:U ! tio 5 !
! <:B x -> /x |x 1 ;(| ||x 3 xD| 1 ;)|\> 5 !
|
In FuncApplyExpr,
the first Expr had better evaluate to a function.
(That is, it might be a FuncExpr,
or an Id which gets substitued to a function value.
It could also be (say) an IfZeroExpr or LetExpr
which evaluates to a function.)
-
First, write the following four functions as R4 programs.
(You can then modify them as part of your tests.)
-
A constant function that always returns (say) 17.
-
the function sqr, which squares its input,
-
the factorial function, written in R4.
Note:
You won't able to evaluate function-applications
for recursive functions yet (see R5),
but we can still write the test cases!
(You can comment out that one test case for now,
since it'll trigger a run-time exception otherwise.)
and
-
The R4 equivalent of the following racket definition make-adder:
(define (make-adder n)
(lambda (m) (+ n m)))
; Two examples of applying this function:
;
(make-adder 3) ; evals to (lambda (m) (+ 3 m))
((make-adder 3) 4) ; evals to 7
|
Then, upgrade R3 so that it implements functions;
label each section of lines you change with a comment “;>>>R4”.
- (2pts) Add a struct/class for representing FuncExprs internally.
- (2pts) expr->string (and tests)
- (6pts) parse! (and tests).
- (2pts) eval (and tests)
-
Implement function-application.
- (2pts) Add a struct/class for representing FuncApplyExprs internally.
- (2pts) parse
/string->expr (and tests)
- (2pts) expr->string (and tests)
- (9pts) eval (and tests).
Here, more than half the points are for tests,
since you want to try several situations involving shadowing variables.
The semantics of eval'ing the function-application
< Expr0 # Expr1 >
! Expr0 Expr1 !:
-
Evaluate Expr0; let’s call the result f.
(f had better be a function-value!)
-
Evaluate Expr1; let’s call the result arg.
-
Substitute f’s parameter with arg in f’s body;
call this new expression E′.
-
Evaluate E′ and return that value.
Hey, those semantics are practically the same as LetExprs’!
Indeed, it's not very different; the function holds the identifier and body;
when you eval a function-application then we do the same substitution.
Observe that our programs can now evaluate to either of two types:
numbers or functions.
In Java,
we'll need a class which can represent either,
as the return type for our eval.
That’s why the abstract class Value was included,
of which Number was one subclass.
Make test cases for
parse!
(at least one, for each of functions and function-applictions).
Then, make test cases for
toString and eval.
You should include the four R4 programs you wrote above,
(but you don['t need to include tests of applying factorial
if you don't want).
Note that we're restricting R4
to only deal with unary functions (functions of one argument).
To think about:
If we wanted to 'fake' functions of 2 arguments in R4,
how could we do it?
For example, you might
think about how to write a function that effectively takes in
two numbers i,j and returns 2·i+j.
Think about how make-adder does this.
- (1pt)
Add another person
'sand at least three more drink preferences to the
Prolog drink-preference knowledge base from lecture.
Make sure that at least two different people like pepsi.
- (5pts)
We say that
chaperone(A,B,C)
(“A and B can be chaperoned by C”)
iff
A,B,C are three different people
who each have some drink preference in common with
each other
(“pairwise”),
and A and B an optimist and a pessimist (in either order).
-
define chaperone(A,B,C)
-
What query will match all the people who
could be a chaperone for alice
and bob?
-
What query will match all pairs of people who
could be chaperoned by dee?
The interpreter project is based on the first chapters of
Programming Languages and Interpretation,
by Shriram Krishnamurthi.
As a result, this homework assignment is covered by the
Creative Commons
Attribution-NonCommercial-ShareAlike 3.0 United States License.
Although we're using a different dialect of racket than that book,
you might find it helpful to skim it.
home—lectures—recipe—exams—hws—D2L—breeze (snow day; distance)