Quiz 3: Control
Instructions:
- Due Date: Monday, Nov. 25, 11:59PM EST
- You must complete this quiz on your own. Do not consult with others. You are encouraged to use the course notes (posted on this webpage) as your main resource when solving these quiz questions.
- All quiz solutions are uploaded to gradescope. There will be two submissions
- A code upload for the programming assignment. Upload two files: a file called
break.rkt
and a file calledtail-form.rkt
. - A file upload where you upload your written solutions. This can be a PDF, photos of paper, etc.
- A code upload for the programming assignment. Upload two files: a file called
- Quiz-related questions will not be answered on Piazza or during office hours. Logistics-related questions (like “how do I turn in the quiz?” or “the autograder does not terminate or errors”) are OK.
Starter code: break.rkt
tail-form.rkt
Question 1: Concept Check [10 Points]
State whether each of the following is true or false. If it is false, provide a brief explanation for why. Upload your solutions as written solutions.
- The following program does not grow the call stack during its execution:
(define (foo k) (if (< 3 k) (foo 25) (+ (foo 30) 15)))
- It is possible to translate any function into one that is in tail form by using continuations.
- The only way to translate a program into tail form is to use continuations.
Question 2: Tail Form [30 Points]
We’ve seen converting functions into tail-recursive form both in class and in a homework. For each of the two functions, write an equivalent implementation in tail-recursive form. Do not remove the original program. Do not change name, arguments or contract of the tail functions, you can only modify the body of them. Only recursive functions need to be transformed into tail recursive form. Starter code is provided in the tail-form.rkt
file.
1.
;;; type tree =
;;; | node of int * (ListOf tree)
;;; | leaf
(struct node (children) #:guard (struct-guard/c (listof (recursive-contract tree/c))) #:transparent)
(struct leaf (val) #:guard (struct-guard/c integer?) #:transparent)
(define tree/c (or/c node? leaf?))
;;; tree-preorder : tree -> ListOf int
;;; Traverse the tree in left to right order and produce
;;; a list of values in the order they were visited.
;;; * do not modify *
(define/contract (tree-preorder tree)
(-> tree/c (listof integer?))
(match tree
[(leaf val) (list val)]
[(node children) (foldr append '() (map tree-preorder children))]))
2.
;;; type lexpr =
;;; | ident of string
;;; | lam of string * lexpr
;;; | app of lexpr * lexpr
;;; | add of lexpr * lexpr
;;; | num of int
(struct ident (s) #:guard (struct-guard/c string?) #:transparent)
(struct lam (id body) #:guard (struct-guard/c string? (recursive-contract expr/c)) #:transparent)
(struct app (e1 e2)
#:guard (struct-guard/c (recursive-contract expr/c) (recursive-contract expr/c))
#:transparent)
(struct num (n) #:guard (struct-guard/c integer?) #:transparent)
(struct add (e1 e2)
#:guard (struct-guard/c (recursive-contract expr/c) (recursive-contract expr/c))
#:transparent)
(define expr/c (or/c ident? lam? app? num? add?))
;;; type value =
;;; | vlam of string * lexpr * environment
;;; | vnum of number
(struct vlam (id body closure)
#:guard (struct-guard/c string? expr/c (hash/c string? (recursive-contract value/c)))
#:transparent)
(struct vnum (n) #:guard (struct-guard/c integer?) #:transparent)
(define value/c (or/c vlam? vnum?))
;;; get−num : value -> number
;;; extracts a number from a value or errors
(define (get-num v)
(match v
[(vnum n) n]
[_ (error "not a number")]))
;;; eval−lam−env : lexpr -> (hash-table string value) -> value
;;; Evaluates the given lexpr into a value
;;; *do not modify*
(define/contract (eval-lam-env e env)
(-> expr/c (hash/c string? value/c) value/c)
(match e
[(ident x) (if (hash-has-key? env x) (hash-ref env x) (error "unbound identifier"))]
[(lam id body) (vlam id body env)]
[(num n) (vnum n)]
[(add e1 e2) (vnum (+ (get-num (eval-lam-env e1 env)) (get-num (eval-lam-env e2 env))))]
[(app e1 e2)
(match (eval-lam-env e1 env)
[(vlam id body closure)
(let* ([arg-v (eval-lam-env e2 env)] [extended-env (hash-set closure id arg-v)])
(eval-lam-env body extended-env))])]))
Question 3 [60 Points]
Imperative languages usually use constructs like for
and while
loops to express iteration. An important part of those constructs is break
, that allows you to ‘break out’ of the loop. In this exercise you will make an interpreter for a small language with while loops and breaks called implang
.
;;; type expr =
;;; | eident of string
;;; | eadd of expr * expr
;;; | elt of expr * expr - means < operator
;;; | eeq of expr * expr - means == operator
;;; | enum of integer
(struct eident (id) #:guard (struct-guard/c string?) #:transparent)
(struct eadd (e1 e2)
#:guard (struct-guard/c (recursive-contract expr/c) (recursive-contract expr/c))
#:transparent)
(struct elt (e1 e2)
#:guard (struct-guard/c (recursive-contract expr/c) (recursive-contract expr/c))
#:transparent)
(struct eeq (e1 e2)
#:guard (struct-guard/c (recursive-contract expr/c) (recursive-contract expr/c))
#:transparent)
(struct enum (n) #:guard (struct-guard/c integer?) #:transparent)
(define expr/c (or/c eident? eadd? elt? eeq? enum?))
;;; type statement =
;;; | sassign of string * expr - id = e
;;; | swhile of expr * statement
;;; | sbreak-if of expr
;;; | sseq of statement * statement
(struct sassign (id e) #:guard (struct-guard/c string? expr/c) #:transparent)
(struct swhile (guard body)
#:guard (struct-guard/c expr/c (recursive-contract statement/c))
#:transparent)
(struct sbreak-if (e) #:guard (struct-guard/c expr/c) #:transparent)
(struct sseq (s1 s2)
#:guard (struct-guard/c (recursive-contract statement/c) (recursive-contract statement/c))
#:transparent)
(define statement/c (or/c sassign? swhile? sbreak-if? sseq?))
;;; type prog = statement * expr
;;; Represents a program in implang that consists of a statement and a return expression.
(struct prog (body ret) #:guard (struct-guard/c statement/c expr/c) #:transparent)
The semantics of the language:
(eident id)
evaluates to the value bound to a variable with thatid
. Raise runtime error if the variable is unbound.(eadd e1 e2)
adds the results of two sub-expressions. Both sub-expressions have to evaluate to numbers, otherwise a runtime error is raised.(elt e1 e2)
compares the results of evaluating two sub-expressions. Letv1
be the result of evaluatinge1
, andv2
ofe2
.v1
andv2
have to evaluate to a number, otherwise a runtime error is raised. Return(vbool #t)
ifv1 < v2
, and(vbool #f)
otherwise.(eeq e1 e2)
compares the results of evaluating two sub-expressions. Letv1
be the result of evaluatinge1
, andv2
ofe2
.v1
andv2
have to evaluate to a number, otherwise a runtime error is raised. Return(vbool #t)
ifv1 = v2
, and(vbool #f)
otherwise.(enum n)
evaluates to(vnum n)
(sassign id e)
mutates the value of the variable. Letv
be the valuee
evaluates to. Then set the value of variableid
to thatv
. Do not check that the type of variable is the same as the type ofe
.(swhile guard body)
runs theguard
. If it evaluates to(vbool #t)
, then it runs the(sseq body (swhile guard body))
. Ifguard
returns(vbool #f)
, then do nothing.(sbreak-if e)
- Ife
evaluates to(vbool #t)
, then terminate the innermost loop only. Ife
evaluates to(vbool #f)
, then do nothing. Does nothing outsideswhile
loop.(sseq s1 s2)
first evaluatess1
, and then evaluatess2
.(prog body ret)
first evaluates statementbody
, and then evaluates expressionret
and returns the result of runningret
.
Some examples:
> (interp/p (prog (sassign "x" (enum 10)) (eident "x")))
(vnum 10)
> (interp/p (prog (sseq (sassign "x" (enum 0))
(swhile (elt (eident "x") (enum 5))
(sassign "x" (eadd (eident "x") (enum 1)))))
(eident "x")))
(vnum 5)
> (interp/p (prog (sseq (sassign "x" (enum 0))
(swhile (elt (eident "x") (enum 5))
(sseq (sbreak-if (elt (enum 3) (eident "x")))
(sassign "x" (eadd (eident "x") (enum 1))))))
(eident "x")))
(vnum 4)
Your task is to implement the interpreter by filling the bodies of functions interp/p
, interp/s
and interp/e
. Do not change the names, arguments or contracts of those functions (contracts and arguments serve as hints as to how you should set up your solution).