λ PPL172 Assignment 2 Responsible TA: Morad Muslimany Submit your answers to the theoretical questions in a pdf file called id1 id2.pdf and your code for programming questions inside the provided hw2.rkt, parser.rkt and rewrite.rkt files in the correct places, and zip those files together (including the pdf file) into a file called id1 id2.zip. Make sure that your code abides the Design By Contract methodology. Do not send assignment-related questions by e-mail, use the forum instead. Theoretical questions are marked with a T, and programming questions are marked with a P. Have fun and may the λs always be by your side. Deadline: 14.05.2017 at 17:00 Question 1 (18 points) 1.1 - T (3 points) What is a special form? 1.2 - T (3 points) Are all atomic expressions primitive? if your answer is ’no’, provide an example. 1.3 - T (3 points) In what cases multiple expressions in the body of a procedure expression (lambda form) are useful? 1.4 - T (3 points) What is a syntactic abbreviation? 1.5 - T (3 points) Recall that the let construct in Scheme is implemented as a syntactic abbreviation. Write the syntactic form the following expression is translated to (you do not have to compute the value of the expression, only show the resulting translated expression): (let ((addsquares (lambda (x y) (+ (* x x) (* y y)))) (add5 (lambda (x) (+ x 5))) (x 120)) 1 (add5 (addsquares (add5 x) (add5 240)))) 1.6 - T (3 points) Consider the following conversion of or expressions to nested if expressions: (or arg1 arg2 ... argN) -> (if arg1 arg1 (if arg2 arg2 ... (if argN argN #f))) Recall our definition of functional equivalence. The concept as we defined it does not include the concept of side-effects. 1.6.1 - T Are the two expressions functionally equivalent according to our definition in class? Prove your answer. 1.6.2 - T Are the two expressions functionally equivalent when considering side-effects as well? Prove your answer. Recall our definition of shortcut semantics. 1.6.3 - T Do or expressions in Racket support shortcut semantics? Prove your answer. 1.6.4 - T Does the translated if expression above support shortcut semantics? Prove your answer. 2 Question 2 (9 points) This question refers to the language L3 as seen in the lectures. Show the steps of evaluating the following expressions according to the evaluation rules for L3 as seen in class. At each step, describe what expression is being evaluated, what type of evaluation is it (atomic/compound special form/compound non-special form), and what is the resulting value. If the return value is received from the global environment, state that fact next to the return value. You are provided with an example and its’ solution in the next page. 2.1 - T (3 points) (define length (lambda (l) (if (empty? l) 0 (+ 1 (length (cdr l)))))) 2.2 - T (3 points) (define length (lambda (l) (if (empty? l) 0 (+ 1 (length (cdr l)))))) (define mylist (cons 1 (cons 2 ’()))) (length mylist) 2.3 - T (3 points) (define x 3) (define y 0) (+ x y y) (lambda (x) (+ x y y)) (lambda (y) (lambda (x) (+ x y y))) ((lambda (x) (+ x y y)) 5 ) ((lambda (y) (lambda (x) (+ x y y))) 2) (((lambda (y) (lambda (x) (+ x y y))) 2) 5) 3 Example (define x -5) (if (> x 0) x (- 0 x)) Example solution evaluate((define x 5)) [compound special form] evaluate(5) [atomic] return value: 5 add the binding <<x>,5> to the GE return value: void evaluate((if (> x 0) x (- 0 x))) [compound special form] evaluate((> x 0)) [compound non-special form] evaluate(>) [atomic] return value: #<procedure:>> evaluate(x) [atomic] return value: 5 (GE) evaluate(0) [atomic] return value: 0 return value: #t evaluate(x) [atomic] return value: 5 (GE) return value: 5 4 Question 3 (6 points) For each of the following pieces of code, and for each binding instance (variable declaration), state its scope and in which lines it appears bound. Furthermore, state which variables are free. If a variable is bound only in some lambda body, state in parenthesis the line number at which the lambda definition begins. Example (define even? (lambda (n) (eq? (modulo n 2) 0))) (even? 5) ;1 ;2 ;3 a possible answer can be: Binding Instance Appears first at line # Scope Line #s of bound occurrences even? n 1 1 Universal Scope Lambda body (1) 3 2 Free variable occurrences: eq?, modulo 3.1 - T (3 points) (define fib (lambda (n) (cond ((= n 0) 0) ((= n 1) 1) (else (+ (fib (- n 1)) (fib (- n 2))))))) (define y 5) (fib (+ y y)) 3.2 - T (3 points) (define triple (lambda (x) (lambda (y) (lambda (z) (+ x y z))))) (((triple 5) 6) 7) 5 ;1 ;2 ;3 ;4 ;1 ;2 ;3 ;4 ;5 ;6 Question 4 (24 points) In each of the following programming questions you may not add helper procedures. Implement them inside the file hw2.rkt in the correct places. You might find the following primitive procedures helpful: append, take, max. 4.1 - P (6 points) Implement the procedure sliding-window that receives a list L and a numeric parameter 1 ≤ window ≤ (length L), and returns a list of lists of length window that contain the elements of L slided. For example: > (sliding-window ’(1 2 3 4 ’((1) (2) (3) (4) (5)) > (sliding-window ’(1 2 3 4 ’((1 2) (2 3) (3 4) (4 5)) > (sliding-window ’(1 2 3 4 ’((1 2 3) (2 3 4) (3 4 5)) > (sliding-window ’(1 2 3 4 ’((1 2 3 4 5)) 5) 1) 5) 2) 5) 3) 5) 5) The following is with no relation to part 4.1: We can represent a (possibly empty or non-complete) binary tree in Scheme as follows: the first element in every nesting level represents the root of the sub-tree. A leaf is represented by an atom. A missing child is represented as the empty list. For example: 5 / \ #t 12 / \ \ 4 2 #f is represented as: ’(5 (#t 4 2) (12 () #f)) 4.2 - P (6 points) Implement the procedure greatest-node(tree) that receives a tree in the representation above whose nodes data are all non-negative numbers, and computes the greatest node data value. If the tree is empty, the return value should be −1. For example: > (define coolTree ’(1 (7 (24 15 ()) (12 25 12)) (12 () ()))) > (greatest-node coolTree) 25 > (greatest-node 5) 5 > (greatest-node ’()) -1 6 4.3 - P (6 points) Implement the procedure count-node(tree,x) that receives a tree in the representation above and an atomic x and computes the number of nodes whose data is equal to x. Note that the tree might contain any atomic data type (number, boolean, symbol, string). For example: > (count-node ’(1 (#f () 1) hi) ’hi) 1 > (count-node ’(1 (#f () 1) hi) 1) 2 > (count-node ’() 20) 0 4.4 - P (6 points) Implement the procedure mirror-tree(tree) that receives a tree in the representation above and computes the mirrored tree. Note that the tree might contain any atomic data type. An example of a tree and its mirror: For the tree: ’(5 (#t 4 2) (12 () 3)) 5 / \ #t 12 / \ \ 4 2 3 the mirrored tree is: ’(5 (12 3 ()) (#t 2 4)) 5 / \ 12 #t / / \ 3 2 4 7 Question 5 (10 points) At times, it is convenient to gradually define variables. Suppose we would like to say that x equals 5 and that y equals x · 3, then one would expect that y equals 15, but if we write the following code, y would be equal to 3 instead: > (define x 1) > (let ((x 5) (y (* x 3))) y) 3 To achieve the result we want, we can nest let expressions as follows: > (let ((x 5)) (let ((y (* x 3))) y)) 15 We can abbreviate the nesting of the let expressions as let*: > (let* ((x 5) (y (* x 3))) y) 15 You are asked to support let* expressions as a syntactic abbreviation. You should extend the parser code shown in class (parser.rkt) and the rewrite methods shown in rewrite.rkt. You must: 1. Extend the BNF of the language to support let* 2. Extend the AST datatype definitions 3. Implement the let* AST functions 4. Implement the rewrite-all-let* function with the following contract: ; Purpose: rewrite expressions containing let*-exp into ASTs that contain only let-exp ; Signature: rewrite-all-let*(exp) ; Type: [CExp -> CExp] The function must replace all let* expressions in the input expression to the nested-let form. Example: > (rewrite-all-let* (parse ’(let* ((x 5) (y (f x))) (+ x y) (- y x)))) ’(let-exp ((binding (var-exp x) (num-exp 5))) 8 ((let-exp ((binding (var-exp y) (app-exp (var-exp f) ((var-exp x))))) ((app-exp (var-exp +) ((var-exp x) (var-exp y))) (app-exp (var-exp -) ((var-exp y) (var-exp x))))))) (question 6 is on the next page) 9 Question 6 (35 points) We have witnessed the power in the concept of syntax driven programming, in which the main idea is to treat programs as data. We have seen both in class and in question 5 how easy it can be to support new expression types, or change the syntactic behavior of the program as we see fit. In this question, we will explore the ability to ”unparse” an AST into a different programming language. This process’ difficulty indeed depends on the lexical and syntactic properties of both the source language and the target language. In the parser code seen in class, there is a ready ”unparse” procedure that when given a Scheme AST as we defined it, unparses it back to its lexical Scheme form. So far in the course, we have seen two programming languages: Javascript and Scheme. In this question, we will write an ”unparse” procedure that, instead of unparsing a Scheme AST into its equivalent Scheme lexical form, unparses it to an (almost) equivalent Javascript lexical form. Almost - because let expressions can not be translated to equivalent Javascript expressions from the point of view of scopes. However, we will ignore that issue. The two programming questions below should be programmed in the file hw2.rkt in the correct places. Part 6.1 - P (25 points) In this part of the question, we will define the following ”unparsing” rules: (define <var> <exp>) -> const <var> = <exp>; <number> -> <number> #t -> true #f -> false = -> == (<proc> <arg1> ... <argN>) -> <proc>(<arg1>,...,<argN>) (if <p> <t> <e>) -> <p> ? <t> : <e> (let ((var1 <exp1>) ... (varN <expN>)) <body1> ... <bodyK>) -> let var1 = <exp1>, ..., varN = <expN>; <body1>; ...; <bodyK>; <string> -> <string> (lambda (param1 ... paramN) <body1> ... <bodyK>) -> (param1,...,paramN) => { <body1>; ...; <bodyK> } 10 Note that the rule: (proc param1 ... paramN) -> proc(param1,...,paramN) applies to the arithmetic operators as well, meaning that in this part of the question we will ignore the infix nature of the arithmetic operators in Javascript. For example, in this part, we will have the following unparsed expressions: (+ 5 x 2) -> +(5,x,2) (+ 5 x (* 2 1 1)) -> +(5,x,*(2,1,1)) We will handle the infix notation in the next part of the question. Implement the procedure unparse->js(ast,output-port) inside parser.rkt that upon receiving a Scheme AST and an output-port, writes to the outputport the equivalent Javascript expression as defined by the rules above. Use the fprintf procedure to write to the output port. You can read more about it here: Racket Documentation. Here are a few examples of using its syntax: (fprintf output-port "const ") (fprintf output-port "~a equals ~a" (get-value x) (get-value y)) (fprintf output-port " ~s " some-string) Note that in Racket, (current-output-port) yields the currently active output port, which usually is the interpreter terminal (bottom part of DrRacket). For clarity, you are provided with a few examples of using unparse->js: > (unparse->js (parse ’(define x (f x (* 5 x) (= 3 2) y))) (current-output-port)) const x = f(x,*(5,x),==(3,2),y); > (unparse->js (parse ’(lambda (x y z) (display "hi!") (if (eq? x y) z (or #f (eq? x z))))) (current-output-port)) (x,y,z) => { display("hi!"); eq?(x,y) ? z : or(false,eq?(x,z)) } > (unparse->js (parse ’(lambda (x) (let ((y (+ (f x) 5)) (z y)) (+ z y) #t))) (current-output-port)) (x) => { let y = +(f(x),5), z = y; +(z,y); true; 11 } > (unparse->js (parse ’(lambda (x y z) x y z)) (current-output-port)) (x,y,z) => { x; y; z } > (unparse->js (parse ’(f (x (g (y (z x)))))) (current-output-port)) f(x(g(y(z(x))))) Part 6.2 - P (10 points) Implement the procedure unparse->js-infix(ast,output-port) that behaves precisely like unparse->js except that it handles correctly the infix nature of the arithmetic operators +,-,*,/,==. That is, the only changed rule is as follows: (proc param1 ... paramN) -> if proc is one of {+,-,*,/,=} then: (param1 proc param2 proc ... proc paramN) otherwise: proc(param1,...,paramN) For example: > (unparse->js-infix (parse ’(+ 5 3 (* 2 (- x y w) 7) z 4)) (current-output-port)) (5 + 3 + (2 * (x - y - w) * 7) + z + 4) > (unparse->js-infix (parse ’(lambda (x y) (if (= x y) (+ y y) (* (- x 0) y (/ x 1 y))))) (current-output-port)) (x,y) => { (x == y) ? (y + y) : ((x - 0) * y * (x / 1 / y)) } Have Fun, λ 12
© Copyright 2024 Paperzz