#lang racket (provide hours depth map-tree sum count-if average lookup join-lists pattern-match evaluate-code) ; ******************************************************** ; CS 201 HW #2 DUE Thursday 2/6/2026, 11:59 pm ; via gradescope, with filename hw2.rkt. ; ******************************************************** ; Name: ; Email address: ; ******************************************************** ; This file may be opened in DrRacket. Lines beginning with ; semicolons are comments. ; If you are asked to write a procedure, please make sure it has the ; specified name, and the specified number and order of arguments. ; The names of the formal arguments need not be the same as in the ; problem specification. ; For each problem, the intended inputs to your procedures are ; specified (for example, "positive integers") and your procedures ; need not do anything reasonable for other possible inputs. ; You may write auxiliary procedures in addition to the requested ; one(s) -- for each of your auxiliary procedures, please include a ; comment explaining what it does, and giving an example or two. ; You may also use procedures you have written elsewhere in this ; assignment or previous assignments. They only need to be defined ; once somewhere within this file. ; Please use the predicate equal? to test equality of values that may ; not be numbers. To test equality of numbers, you can use =. ; Please do not use the function flatten on the first five problems, ; which ask you to process nested lists. (You may write a version of ; flatten yourself if you'd like, though it's not necessary to solve ; the problems.) ; ******************************************************** ; ** problem 0 ** (1 easy point) ; Replace the number 0 in the definition below to indicate ; the number of hours you spent doing this assignment ; Decimal numbers (eg, 6.237) are fine. (define hours 0) ; ******************************************************** ; ** problem 1 ** (11 points) ; Write a procedure ; (depth tree) ; which takes a tree (i.e., nested list) as input and return an integer ; indicating the maximum level of the tree (the maximum number of parentheses ; that are open at any point). ; Examples ; (depth '()) => 0 ; (depth '(1 2 3)) => 1 ; (depth '(a (b (c (d))))) => 4 ; (depth '((((((0))))))) => 6 ; ******************************************************** ; depth : tree -> number (define depth empty) ; ******************************************************** ; ** problem 2 ** (11 points) ; Write a procedure ; (map-tree proc tree) ; which takes two arguments, a procedure proc and a nested list tree, ; and returns a copy of tree with each leaf node replaced by ; the result of applying proc to that leaf. ; Examples: ; (map-tree even? '(1 2 3 4)) => '(#f #t #f #t) ; (map-tree even? '(1 (2 (3 (4))))) => '(#f (#t (#f (#t)))) ; (map-tree (lambda (x) (+ x 1)) '(1 (2 (3 (4 5 6))))) => '(2 (3 (4 (5 6 7)))) ; (map-tree odd? '()) => '() ; (map-tree symbol? '((a b 2) 3 4 ((5)))) => '((#t #t #f) #f #f ((#f))) ; ******************************************************** ; map-tree : (A -> B) (tree-of A) -> (tree-of B) (define map-tree empty) ; ******************************************************** ; ** problem 3 ** (11 points) ; Write a procedure ; (sum tree) ;; which returns the total of the numeric leaves of the tree. ;; That is, add up all the numeric leaves. ;; Examples: ;; (sum '(1 2 3 4)) => 10 ;; (sum '(a (1 (b (2 (3 "four")))))) => 6 ;; (sum '(((((((((8)))))))))) => 8 ;; (sum '((((((((()))))))))) => 0 ; ******************************************************** ; sum : tree -> number (define sum empty) ; ******************************************************** ; ** problem 4 ** (11 points) ; Write a procedure ; ; (count-if pred tree) ; ; which returns the number of leaves of the tree that satisfy ; the given predicate pred ; ; Examples ; ; (count-if odd? '(1 2 3)) => 2 ; (count-if even? '(1 2 3)) => 1 ; (count-if integer? '(1 (2 (3)))) => 3 ; (count-if string? '()) => 0 ; (count-if even? '((((((8 8 8))))))) => 3 ; (count-if (lambda (x) (> x 5)) '((((((9 9 9))))))) => 3 ; ; ******************************************************** ; count-if : (any -> boolean) tree -> number (define count-if empty) ; ******************************************************** ; ** problem 5 ** (11 points) ; Write a procedure ; (average tree) ;; that takes a nested list tree and returns the average of the ;; numeric elements of the tree. ;; If the tree contains no elements or no numeric elements, return 'NA ;; Examples ;; (average '(1 2 3 4 5)) => 3 ;; (average '(1 2 3 4 5 a b c d e)) => 3 ;; (average '(1 (2 (3 d e f) (4 5 6 7)))) => 4 ;; (average '((()))) => 'NA ;; (average '(a b c d e)) => 'NA ;; (average '(a b c d e 2 3 4)) => 3 ;; (average '()) => 'NA ;; (average '(1.2 1.3 1.4 1.5)) => 1.35 ; ******************************************************** ; average : tree -> number-or-NA (define average empty) ; ******************************************************** ; ** problem 6 ** (11 points) ; Write a procedure (lookup env symbol) that looks up the value ; of a symbol in an environment. An environment is a nested list, ; where each element is a list of the form (symbol value). For ; example, '((x 3) (y 2) (another-symbol (1 2 3))) is an environment ; with three bindings: ; 'x ==> 3 ; 'y ==> 2 ; 'another-symbol ==> '(1 2 3) ; The job of the lookup procedure is to search the environment for ; a binding for a particular symbol, and return the associated value. ; If there is no binding for the given symbol, you should return ; the symbol 'error. ; ; Examples: ; ; (lookup '((x 2) (y 5)) 'y) => 5 ; (lookup '() 'z) => 'error ; (lookup '((a b) (b 3)) 'a) => 'b ; (lookup '((a (b (c))) (d (e (f)))) 'a) => '(b (c)) ; (lookup '((a (b (c))) (d (e (f)))) 'd) => '(e (f)) ; (lookup '((a (b (c))) (d (e (f)))) 'b) => 'error ; ; ******************************************************** ; lookup : environment symbol -> any (define lookup empty) ; ********************************************************* ; ** problem 7 ** (11 points) ; Write a procedure (join-lists lists) that takes in a list ; where each element is either #f or another list. If any of ; the elements are #f, join-lists also returns #f. If all of ; the elements are lists, then join-lists concatenates all ; the lists into one large list. ; Examples: ; ; (join-lists '()) => '() ; (join-lists '((1 2 3) (4 5) (#t #f))) => '(1 2 3 4 5 #t #f) ; (join-lists '((1 2 3) (4 5) #f (#t))) => #f ; (join-lists '(((1) (2)) (3 4) (((5)))) => '((1) (2) 3 4 ((5))) ; ; ******************************************************** ; join-lists : (list-of list-or-#f) -> list-or-#f (define join-lists empty) ; ******************************************************** ; In the following problems, an "expression" is either: ; - a number; ; - a symbol; ; - a boolean; ; - or a list of expressions. ; Here is a helper function you will need in the next problem: ; is-uppercase-symbol? : any -> boolean ; Checks whether a value is an uppercase symbol, i.e. a symbol ; that contains no lowercase letters. ; Examples: ; - (is-uppercase-symbol? 3) => #f ; - (is-uppercase-symbol? 'hEllo) => #f ; - (is-uppercase-symbol? 'HELLO) => #t ; - (is-uppercase-symbol? 'HELLO-THERE-42) => #t (define (is-uppercase-symbol? s) (and (symbol? s) (equal? (symbol->string s) (string-upcase (symbol->string s))))) ; ********************************************************* ; ** problem 8 ** (11 points) ; In this problem, you will be writing a pattern matcher, that ; takes in two expressions (a pattern and a test expression), ; and tries to "match" them. The result is either #f (if the match ; fails), or an environment, mapping uppercase symbols to their matched ; expressions. In more detail, given two expressions `pattern` and `expr`: ; ; - if `pattern` is a number and `expr` is the same number, the match succeeds with no bindings ; - if `pattern` is a boolean and `expr` is the same boolean, the match succeeds with no bindings ; - if `pattern` is a *non-uppercase* symbol and `expr` is the same symbol, the match succeeds with no bindings ; - if `pattern` is an *uppercase* symbol, the match succeeds with a single binding: pattern is bound to expr ; - if `pattern` is a list, and expr is a list of the same length, the match succeeds if each element of the ; list `pattern` successfully matches the corresponding element of the list `expr`. In that case, the match ; succeeds with the union of all bindings from the sub-matches. ; ; Your job is to write a procedure (pattern-match pattern expr). ; Given a pattern and an expression, the procedure should return #f if pattern fails ; to match expr. Otherwise, it should return a list of bindings that the match generates, ; one for each uppercase symbol appearing in pattern. ; You may assume that the uppercase symbols appearing in pattern are unique (none are used ; twice). ; Examples: ; ; (pattern-match 'X 3) => '((X 3)) ; (pattern-match 52 52) => '() ; (pattern-match 42 52) => #f ; (pattern-match '() '()) => '() ; (pattern-match '(X (Y) ((Z))) '(1 (2) ((3)))) => '((X 1) (Y 2) (Z 3)) ; (pattern-match '(X (Y) ((Z))) '((1) ((2)) (((3))))) => '((X (1)) (Y (2)) (Z (3))) ; (pattern-match '(X (Y) ((Z))) '(1 (2) (3))) => #f ; (pattern-match '(X Y Z) '(1 2)) => #f ; (pattern-match '(do (X) and (Y) sum to 20) '(do ((* 2 3)) and (14) sum to 20)) => '((X (* 2 3)) (Y 14)) ; (pattern-match '(either BOOL-1 or BOOL-2) '(either (the-number 5 is-less-than 3) or (the-number 6 is-greater-than 4))) ; => '((BOOL-1 (the-number 5 is-less-than 3)) (BOOL-2 (the-number 6 is-greater-than 4))) ; (pattern-match '(compute NUM-1 OP-NAME NUM-2) '(compute (compute 7 times 3) minus 8)) ; => '((NUM-1 (compute 7 times 3)) (OP-NAME minus) (NUM-2 8)) ; (pattern-match '((X)) '(())) => #f ; (pattern-match '(in-the-case-that BOOL EXPR-1 and-otherwise EXPR-2) ; '(in-the-case-that (the-number x is-less-than y) x and-otherwise y)) ; => '((BOOL (the-number x is-less-than y)) (EXPR-1 x) (EXPR-2 y)) ; (pattern-match '(X is a Y) '(Racket is a language)) => '((X Racket) (Y language)) ; (pattern-match '(3 is-equal-to SOMETHING) '(3 is-equal-to (+ 1 2))) => '((SOMETHING (+ 1 2))) ; ; Hint: you may find the `join-lists` function useful for joining the sub-match results into ; results for this match. ; ; ******************************************************** ; pattern-match : expression expression -> list-or-false (define pattern-match empty) ; ********************************************************* ; ** problem 9 ** (11 points) ; ; You will now write an interpreter for a made-up programming ; language called "Verbose Racket". Verbose Racket programs are ; written as quoted lists, and look like this: ; '(compute 3 plus 4) => 7 ; '(in-the-case-that (the-number 6 is-less-than 8) ; (compute 7 times 3) and-otherwise (just-return 2)) => 21 ; '(either (the-number 6 is-equal-to (compute 3 times 2)) or (the-number 7 is-less-than 3)) => #t ; As you can see, it's a programming language kind of like Racket, with parenthesized expressions, ; but there's a lot more verbiage ("in-the-case-that", "and-otherwise", etc. instead of Racket's "if"). ; Formally, Verbose Racket language supports the following expressions: ; (compute NUM-EXPR-1 OP-NAME NUM-EXPR-2), where OP-NAME can be 'plus, 'times, 'minus, 'divided-by ; applies the named operation to the two numeric expressions ; (the-number NUM-EXPR-1 COMPARISON NUM-EXPR-2), where COMPARISON can be 'is-greater-than, 'is-less-than, or 'is-equal-to ; returns the Boolean result of comparing the two numeric expressions ; (either BOOL-EXPR-1 or BOOL-EXPR-2) ; returns true if either BOOL-EXPR-1 is true, or BOOL-EXPR-1 is false and BOOL-EXPR-2 is true. ; otherwise, returns false (or 'error if an evaluated expression causes an error.) ; (both BOOL-EXPR-1 and BOOL-EXPR-2) ; returns false if either BOOL-EXPR-1 is false, or BOOL-EXPR-1 is true but BOOL-EXPR-1 is false. ; otherwise, returns true (or 'error if an evaluated expression causes an error.) ; (in-the-case-that BOOL-EXPR THEN-BRANCH and-otherwise ELSE-BRANCH) ; if BOOL-EXPR is true, returns THEN-BRANCH, else it returns ELSE-BRANCH. ; if BOOL-EXPR has an error or the evaluated branch does, return 'error. ; (just-return EXPR) ; evaluates and returns EXPR. ; variable (written as a symbol) ; looks up the value of the var in the environment, or returns 'error if undefined ; literal number or literal boolean ; evaluates to itself ; For example, the following computes the absolute value of a variable x: ; '(in-the-case-that (the-number x is-less-than 0) (compute 0 minus x) and-otherwise (just-return x)) ; Your job is to write a function (evaluate-code expr env). It takes in a Verbose Racket expression, ; as well as an environment binding variable names to literal values. It then evaluates the Verbose ; Racket expression, using the given environment to look up any variables. ; If the Verbose Racket expression is ill-formed, or uses a variable that is not in env, then your ; procedure should return the symbol 'error. (An expression is "ill-formed" if it doesn't follow one ; of the above patterns.) ; ; Examples: ; ; (evaluate-code 'z '((z 3))) => 3 ; (evaluate-code 'z '((x 3))) => 'error ; (evaluate-code '(compute z plus 3) '((z 7))) => 10 ; (evaluate-code '(plz-compute z plus 3) '((z 7))) => 'error ; (evaluate-code '(the-number 6 is-less-than 7) '()) => #t ; (evaluate-code '(either #f or x) '((x #t))) => #t ; (evaluate-code '(both #f and x) '((x #t))) => #f ; (evaluate-code '(in-the-case-that (the-number (compute x plus x) is-greater-than 10) (just-return 'z) and-otherwise (just-return 'y)) ; '((x 6) (y 2) (z 8))) => 8 ; (evaluate-code '(either #f or-possibly x) '((x #t))) => 'error ; (evaluate-code '(compute (compute celsius times 1.8) plus 32) '((celsius 100.0))) => 212.0 ; ; We provide the following helper functions: ; ; lookup-operation-by-name : symbol -> (any any -> number-or-error) ; e.g.: ((lookup-operation-by-name 'plus) 2 3) => 5 ; ((lookup-operation-by-name 'add) 2 3) => 'error ; ((lookup-operation-by-name 'times) "hello" #t) => 'error (define (lookup-operation-by-name name) ; Wrap an operator so it safely propagates 'error and type errors. ; The returned function accepts any two values ; and returns either a number or 'error. (define (wrap op) (lambda (v1 v2) (cond [(not (and (number? v1) (number? v2))) 'error] [else ; handle things like divide-by-zero (with-handlers ([exn:fail? (lambda (_e) 'error)]) (define res (op v1 v2)) (if (number? res) res 'error))]))) (cond [(equal? name 'plus) (wrap +)] [(equal? name 'minus) (wrap -)] [(equal? name 'times) (wrap *)] [(equal? name 'divided-by) (wrap /)] [else (lambda (_n1 _n2) 'error)])) ; lookup-comparison-by-name : symbol -> (any any -> boolean-or-error) ; e.g.: ((lookup-comparison-by-name 'is-greater-than) 2 3) => #f ; ((lookup-comparison-by-name 'is-taller-than) 2 3) => 'error ; ((lookup-comparison-by-name 'is-less-than) 2 "three") => 'error (define (lookup-comparison-by-name name) ; Wrap a comparison so it safely propagates 'error and type errors. ; The returned function accepts any two values and returns either a boolean or 'error. (define (wrap cmp) (lambda (v1 v2) (cond [(not (and (number? v1) (number? v2))) 'error] [else (with-handlers ([exn:fail? (lambda (_e) 'error)]) (define res (cmp v1 v2)) (if (boolean? res) res 'error))]))) (cond [(equal? name 'is-less-than) (wrap <)] [(equal? name 'is-greater-than) (wrap >)] [(equal? name 'is-equal-to) (wrap =)] [else (lambda (_n1 _n2) 'error)])) ; ******************************************************** ; evaluate-code : expression (list-of list) -> number-or-boolean-or-error (define evaluate-code empty) ; ******************************************************** ; ******** testing, testing. 1, 2, 3 .... ; ******************************************************** ; Press Run in DrRacket, and then type (runtests) in the ; Interactions area to run automated tests on your code. (define (format-expected-actual expected actual) (format "Correct answer should be ~a. Your answer was: ~a." expected actual)) (define (approx=? a b [eps 1e-9]) (and (real? a) (real? b) (<= (abs (- (exact->inexact a) (exact->inexact b))) eps))) (struct test-result (name ok? got expected output) #:transparent) (define (test name got-thunk expected [expected-text #f]) (define expected-display (cond [expected-text expected-text] [(procedure? expected) "a value satisfying the expected predicate"] [else expected])) (with-handlers ([exn:fail? (lambda (e) (test-result name #f (list 'error (exn-message e)) expected-display (format "Error while running: ~a" (exn-message e))))]) (define got (got-thunk)) (define ok? (if (procedure? expected) (with-handlers ([exn:fail? (lambda (_e) #f)]) (expected got)) (equal? got expected))) (test-result name ok? got expected-display (format-expected-actual expected-display got)))) (define (print-test-result r) (define status (if (test-result-ok? r) "OK" "FAIL")) (printf "[~a] ~a\n" status (test-result-name r)) (when (not (test-result-ok? r)) (printf " expected: ~a\n" (test-result-expected r)) (printf " got: ~a\n" (test-result-got r)) (printf " ~a\n" (test-result-output r)))) ; helper to compare two lists disregarding order. (define (same-elements? xs ys) (and (list? xs) (list? ys) (= (length xs) (length ys)) (andmap (lambda (x) (member x ys)) xs) (andmap (lambda (y) (member y xs)) ys))) (define (runtests) (define results (list (test 'hours (lambda () hours) (lambda (x) (and (number? x) (> x 0))) "a positive number") ;; problem 1: depth (test 'depth-empty (lambda () (depth '())) 0) (test 'depth-flat (lambda () (depth '(1 2 3))) 1) (test 'depth-nested (lambda () (depth '(a (b (c (d)))))) 4) (test 'depth-deep (lambda () (depth '((((((0)))))))) 6) ;; problem 2: map-tree (test 'map-tree-even-flat (lambda () (map-tree even? '(1 2 3 4))) '(#f #t #f #t)) (test 'map-tree-even-nested (lambda () (map-tree even? '(1 (2 (3 (4)))))) '(#f (#t (#f (#t))))) (test 'map-tree-add1 (lambda () (map-tree (lambda (x) (+ x 1)) '(1 (2 (3 (4 5 6)))))) '(2 (3 (4 (5 6 7))))) (test 'map-tree-empty (lambda () (map-tree odd? '())) '()) (test 'map-tree-symbol? (lambda () (map-tree symbol? '((a b 2) 3 4 ((5))))) '((#t #t #f) #f #f ((#f)))) ;; problem 3: sum (test 'sum-flat (lambda () (sum '(1 2 3 4))) 10) (test 'sum-mixed (lambda () (sum '(a (1 (b (2 (3 "four"))))))) 6) (test 'sum-deep (lambda () (sum '(((((((((8))))))))))) 8) (test 'sum-empty-nested (lambda () (sum '((((((((())))))))))) 0) ;; problem 4: count-if (test 'count-if-odd (lambda () (count-if odd? '(1 2 3))) 2) (test 'count-if-even (lambda () (count-if even? '(1 2 3))) 1) (test 'count-if-integer (lambda () (count-if integer? '(1 (2 (3))))) 3) (test 'count-if-string-empty (lambda () (count-if string? '())) 0) (test 'count-if-even-many (lambda () (count-if even? '((((((8 8 8)))))))) 3) (test 'count-if->5 (lambda () (count-if (lambda (x) (> x 5)) '((((((9 9 9)))))))) 3) ;; problem 5: average (test 'average-simple (lambda () (average '(1 2 3 4 5))) 3) (test 'average-ignore-non-numbers (lambda () (average '(1 2 3 4 5 a b c d e))) 3) (test 'average-nested (lambda () (average '(1 (2 (3 d e f) (4 5 6 7))))) 4) (test 'average-empty-nested (lambda () (average '((())))) 'NA) (test 'average-no-numbers (lambda () (average '(a b c d e))) 'NA) (test 'average-some-numbers (lambda () (average '(a b c d e 2 3 4))) 3) (test 'average-empty (lambda () (average '())) 'NA) (test 'average-inexact (lambda () (average '(1.2 1.3 1.4 1.5))) (lambda (x) (approx=? x 1.35)) 1.35) ;; problem 6: lookup (test 'lookup-present (lambda () (lookup '((x 2) (y 5)) 'y)) 5) (test 'lookup-missing (lambda () (lookup '() 'z)) 'error) (test 'lookup-symbol-value (lambda () (lookup '((a b) (b 3)) 'a)) 'b) (test 'lookup-list-value (lambda () (lookup '((a (b (c))) (d (e (f)))) 'a)) '(b (c))) (test 'lookup-list-value-2 (lambda () (lookup '((a (b (c))) (d (e (f)))) 'd)) '(e (f))) (test 'lookup-not-present (lambda () (lookup '((a (b (c))) (d (e (f)))) 'b)) 'error) ;; problem 7: join-lists (test 'join-lists-empty (lambda () (join-lists '())) '()) (test 'join-lists-all-lists (lambda () (join-lists '((1 2 3) (4 5) (#t #f)))) '(1 2 3 4 5 #t #f)) (test 'join-lists-has-false (lambda () (join-lists '((1 2 3) (4 5) #f (#t)))) #f) (test 'join-lists-nested (lambda () (join-lists '(((1) (2)) (3 4) (((5)))))) '((1) (2) 3 4 ((5)))) ;; problem 8: pattern-match (test 'pattern-match-upper-symbol (lambda () (pattern-match 'X 3)) '((X 3))) (test 'pattern-match-number (lambda () (pattern-match 52 52)) '()) (test 'pattern-match-number-fail (lambda () (pattern-match 42 52)) #f) (test 'pattern-match-empty-lists (lambda () (pattern-match '() '())) '()) (test 'pattern-match-nested-ok (lambda () (pattern-match '(X (Y) ((Z))) '(1 (2) ((3))))) (lambda (xs) (same-elements? xs '((X 1) (Y 2) (Z 3)))) "expected bindings (any order): '((X 1) (Y 2) (Z 3))") (test 'pattern-match-nested-list-bindings (lambda () (pattern-match '(X (Y) ((Z))) '((1) ((2)) (((3)))))) (lambda (xs) (same-elements? xs '((X (1)) (Y (2)) (Z (3))))) "expected bindings (any order): '((X (1)) (Y (2)) (Z (3)))") (test 'pattern-match-nested-fail (lambda () (pattern-match '(X (Y) ((Z))) '(1 (2) (3)))) #f) (test 'pattern-match-length-fail (lambda () (pattern-match '(X Y Z) '(1 2))) #f) (test 'pattern-match-do-and (lambda () (pattern-match '(do (X) and (Y) sum to 20) '(do ((* 2 3)) and (14) sum to 20))) (lambda (xs) (same-elements? xs '((X (* 2 3)) (Y 14)))) "expected bindings (any order): '((X (* 2 3)) (Y 14))") (test 'pattern-match-either-bools (lambda () (pattern-match '(either BOOL-1 or BOOL-2) '(either (the-number 5 is-less-than 3) or (the-number 6 is-greater-than 4)))) (lambda (xs) (same-elements? xs '((BOOL-1 (the-number 5 is-less-than 3)) (BOOL-2 (the-number 6 is-greater-than 4))))) "expected bindings (any order): '((BOOL-1 (the-number 5 is-less-than 3)) (BOOL-2 (the-number 6 is-greater-than 4)))") (test 'pattern-match-compute-pattern (lambda () (pattern-match '(compute NUM-1 OP-NAME NUM-2) '(compute (compute 7 times 3) minus 8))) (lambda (xs) (same-elements? xs '((NUM-1 (compute 7 times 3)) (OP-NAME minus) (NUM-2 8)))) "expected bindings (any order): '((NUM-1 (compute 7 times 3)) (OP-NAME minus) (NUM-2 8))") (test 'pattern-match-double-parens-fail (lambda () (pattern-match '((X)) '(()))) #f) (test 'pattern-match-if-pattern (lambda () (pattern-match '(in-the-case-that BOOL EXPR-1 and-otherwise EXPR-2) '(in-the-case-that (the-number x is-less-than y) x and-otherwise y))) (lambda (xs) (same-elements? xs '((BOOL (the-number x is-less-than y)) (EXPR-1 x) (EXPR-2 y)))) "expected bindings (any order): '((BOOL (the-number x is-less-than y)) (EXPR-1 x) (EXPR-2 y))") (test 'pattern-match-x-is-a-y (lambda () (pattern-match '(X is a Y) '(Racket is a language))) (lambda (xs) (same-elements? xs '((X Racket) (Y language)))) "expected bindings (any order): '((X Racket) (Y language))") (test 'pattern-match-3-is-equal-to (lambda () (pattern-match '(3 is-equal-to SOMETHING) '(3 is-equal-to (+ 1 2)))) (lambda (xs) (same-elements? xs '((SOMETHING (+ 1 2))))) "expected bindings: '((SOMETHING (+ 1 2)))") ;; problem 9: evaluate-code (all examples + extra edge cases) (test 'evaluate-code-quote-compute-example (lambda () (evaluate-code '(compute 3 plus 4) '())) 7) (test 'evaluate-code-quote-if-example (lambda () (evaluate-code '(in-the-case-that (the-number 6 is-less-than 8) (compute 7 times 3) and-otherwise (just-return 2)) '())) 21) (test 'evaluate-code-quote-either-example (lambda () (evaluate-code '(either (the-number 6 is-equal-to (compute 3 times 2)) or (the-number 7 is-less-than 3)) '())) #t) (test 'evaluate-code-var (lambda () (evaluate-code 'z '((z 3)))) 3) (test 'evaluate-code-var-missing (lambda () (evaluate-code 'z '((x 3)))) 'error) (test 'evaluate-code-compute (lambda () (evaluate-code '(compute z plus 3) '((z 7)))) 10) (test 'evaluate-code-compute-missing-var (lambda () (evaluate-code '(compute z plus 3) '())) 'error) (test 'evaluate-code-compute-bad-op (lambda () (evaluate-code '(compute 1 add 2) '())) 'error) (test 'evaluate-code-compute-bad-shape-1 (lambda () (evaluate-code '(compute 1 plus) '())) 'error) (test 'evaluate-code-compute-bad-shape-2 (lambda () (evaluate-code '(compute 1 plus 2 3) '())) 'error) (test 'evaluate-code-compute-non-number-1 (lambda () (evaluate-code '(compute #t plus 2) '())) 'error) (test 'evaluate-code-compute-non-number-2 (lambda () (evaluate-code '(compute 1 plus #f) '())) 'error) (test 'evaluate-code-bool-literal (lambda () (evaluate-code '(the-number 6 is-less-than 7) '())) #t) (test 'evaluate-code-compare-bad-op (lambda () (evaluate-code '(the-number 1 is-taller-than 2) '())) 'error) (test 'evaluate-code-compare-non-number (lambda () (evaluate-code '(the-number #t is-less-than 2) '())) 'error) (test 'evaluate-code-either (lambda () (evaluate-code '(either #f or x) '((x #t)))) #t) (test 'evaluate-code-either-non-bool (lambda () (evaluate-code '(either 1 or #f) '())) 'error) (test 'evaluate-code-either-wrong-keyword (lambda () (evaluate-code '(either #t and #f) '())) 'error) (test 'evaluate-code-either-short-circuit (lambda () (evaluate-code '(either #t or (compute z plus 1)) '())) #t) (test 'evaluate-code-both (lambda () (evaluate-code '(both #f and x) '((x #t)))) #f) (test 'evaluate-code-both-non-bool (lambda () (evaluate-code '(both 0 and #t) '())) 'error) (test 'evaluate-code-both-wrong-keyword (lambda () (evaluate-code '(both #t or #f) '())) 'error) (test 'evaluate-code-both-short-circuit (lambda () (evaluate-code '(both #f and (compute z plus 1)) '())) #f) (test 'evaluate-code-if (lambda () (evaluate-code '(in-the-case-that (the-number (compute x plus x) is-greater-than 10) (just-return z) and-otherwise (just-return y)) '((x 6) (y 2) (z 8)))) 8) (test 'evaluate-code-if-non-bool-condition (lambda () (evaluate-code '(in-the-case-that 1 (just-return 2) and-otherwise (just-return 3)) '())) 'error) (test 'evaluate-code-abs-example (lambda () (evaluate-code '(in-the-case-that (the-number x is-less-than 0) (compute 0 minus x) and-otherwise (just-return x)) '((x -5)))) 5) (test 'evaluate-code-ill-formed (lambda () (evaluate-code '(plz-compute z plus 3) '((z 7)))) 'error) (test 'evaluate-code-empty-list (lambda () (evaluate-code '() '())) 'error) (test 'evaluate-code-unknown-form (lambda () (evaluate-code '(z) '((z 3)))) 'error) (test 'evaluate-code-bad-either (lambda () (evaluate-code '(either #f or-possibly x) '((x #t)))) 'error) (test 'evaluate-code-just-return-bad-arity-0 (lambda () (evaluate-code '(just-return) '())) 'error) (test 'evaluate-code-just-return-bad-arity-2 (lambda () (evaluate-code '(just-return 1 2) '())) 'error) (test 'evaluate-code-nested-compute (lambda () (evaluate-code '(compute (compute celsius times 1.8) plus 32) '((celsius 100.0)))) (lambda (x) (approx=? x 212.0)) 212.0))) (displayln "Running tests...") (for-each print-test-result results) (define passed (length (filter test-result-ok? results))) (printf "\nPassed ~a / ~a tests.\n" passed (length results)))