#lang racket (require test-engine/racket-gui) ; (for a beginning-student version, see 2017fall version of file.) ; task: write `quadratic`. easiest test-cases? ; ; e.g. x^2 = 0 (that is: a=1,b=0,c=0) ; e.g. x^2 - 4 = 0 ? (that is: a=?,b=?,c=?) ; e.g. x^2 - 7x + 12 = 0 (answer? Hint: lhs factors in to (x-3)(x-4)) (check-expect (quadratic_v2 1 0 0) (list 0 0)) (check-expect (quadratic_v2 1 0 -4) (list 2 -2)) ; real, real, real -> list-of-length-exactly-2-numbers ; (define (quadratic_v0 a b c) (list (/ (+ (- b) (sqrt (- (sqr b) (* 4 a c)))) (* 2 a)) (/ (- (- b) (sqrt (- (sqr b) (* 4 a c)))) (* 2 a)))) ; Now, let's re-factor the repeated code. ; one way: a helper. But we have a new way, today: let ; use `let` to reduce repeated code! (define (quadratic_v99 a b c) (let {[4ac (* 4 a c)]} (let {[discriminant (- (sqr b) 4ac)] [denom (* 2 a)] } (list (/ (+ (- b) (sqrt discriminant)) denom) (/ (- (- b) (sqrt discriminant)) denom))))) ; use `let` to reduce repeated code! (define (quadratic_v1 a b c) (let* {[discriminant (- (sqr b) (* 4 a c))] [denom (* 2 a)] } (list (/ (+ (- b) (sqrt discriminant)) denom) (/ (- (- b) (sqrt discriminant)) denom)))) ; use `lambda` to reduce repeated code further! (define (quadratic_v2 a b c) (let* {[discriminant (- (sqr b) (* 4 a c))] [denom (* 2 a)] [combine-via (λ (pm) (/ (pm (- b) (sqrt discriminant)) denom))] } (list (combine-via +) (combine-via -)))) ;; Def'n [Scott, Sect.3.3]: Scope: ;; The textual region of a program in which a binding is active. ; ; scope - where can a var be "seen" ; one var may "shadow" another [explain] ; ; binding occurence: declaring a new var ; bound occurrence: using a particular var. ; (show arrows in drracket) ; BUT What if I wanted: (define (quadratic_v3 a b c) (let* {[discriminant (- (sqr b) (* 4 a c))] [denom (* 2 a)] [sqrt-disc (sqrt discriminant)]} ;<--- what does this mean, for `let`? (cons (/ (+ (- b) sqrt-disc) denom) (cons (/ (- (- b) sqrt-disc) denom) '())))) ;; ;; ; Syntax of `let`: ; -> ( let { } ) ; ; -> [ ] ; -> ϵ | ; Semantics of `let` (informal): ; ; - first eval each Binding's Expr_1, Expr_2, ..., Expr_n ; remembering the answer as Val_1, Val_2, ..., Val_n ; - in the (body) Expr, substitute each Id_i with its corresponding Val_i ; - evaluate that (substituted) Expr and return the resulting value. ; Semantics of `let` (more formal): ; - eval'ing `(let {} )` is the result of eval'ing ``. ; - eval'ing `(let {[ ] } )` is ; the result of eval'ing ; `(let {Bindings} )` ; where is just like except ; every occurrence(*) of in has been replaced with . ; You can think of calling a function `(subst (eval ) ))` ; ; (*) technically: "every *unbound* occurrence ..." -- more later. (let {[n 3]} (+ 3 (let {[k (* 7 n)]} (* k 2)) n)) ; In `let`, the scope of the introduced-bindings is the let's body. (let {[n 3]} (+ 3 (let {[k (* 7 n)]} (* k (let {[j 99]} (/ j 3)) 2)) n)) ; is equivalent to: (let* {[n j] [k (* 7 n)] [j 99]} (+ 3 (* k (/ j 3)) 2)) n)) #| class Foo { void blah() { for (int i=0; i<100; ++i) { int j = i; System.out.println(i); } System.out.println(j); // syntax error -- j not in scope System.out.println(i); // syntax error -- i not in scope } } |# #| You can also have shadowing: [1] (let {[n 3]} [2] (let {[n 5]} [3] (* n 2))) returns 10; the binding-occurence on line [2] is what is used on line [3]; I'll write "[2] -> [3]" (non-standard terminology). [1] (let {[n 3]} [2] (+ (let {[n 5]} [3] (* n 2))) [4] n) evaluates to 13 -- [2]->[3], and [1]->[4]. So the scope of the `n` on [2] is line [3] (but not [2]). So the scope of the `n` one line[1] is lines[2]-[4] ... except we say that the inner `n` "shadows" the outer one, for line[3]. ; Lisp/Scheme/Racket have "let*" which corresponds to nested-lets: ; ; #| Compare to java -- a very common pitfall for beginners: class Stuff { int[] nums; Stuff() { int[] nums = new int[365]; for (int i=0; i