;; CS101, Spring 2013 ;; Lecture #8 ;; ;; More recursion! ;; ; Mathematicians tell us that the number PI is approximated ; by sums of the form: ; ; 4 - 4/3 + 4/5 - 4/7 + 4/9 - ... (+/-) 4/n ; ; In particular, as the value of n gets larger, the value of ; the sum gets closer to PI. ; ; Notice that the terms in odd positions are added to the ; final value, and the terms in even positions are subtracted. ; What value of n corresponds to the first term? To the second ; term? How will n change for each recursive call? ; ; How will we distinguish the case where we add a term from ; the case in which we subtract a term? HINT: Use the ; remainder function to find the value returned from calcu- ; lating (remainder n 4). (remainder n 4) can only return ; 4 possible values for any positive value of n: 0, 1, 2, or 3. ; (remainder n 4) returns 0 or 2 only when n is even, which ; it won't be in this approximation. That leaves us with ; the cases in which (remainder n 4) returns either 1 or 3. ; ; What is returned by the function (remainder n 4) when we ; need to add a term in this equation? What is returned by ; (remainder n 4) when we need to subtract a term? ; Define a regular (suspended operation-style) recursive function ; to produce a sum to approximate pi, given n, a non-negative integer. ; CONTRACT: (approx-pi-n pos-odd-integer) -> number ; HEADER: (define approx-pi-n (lambda (n) ...)) ; PURPOSE: Output is equal to the sum ; 4 - 4/3 + 4/5 - 4/7 + ... (+/-)4/n ; which closely approximates PI for large enough n. ; ; PRE-FUNCTION TESTS: Need check-within instead of check-expect (check-within (approx-pi-n 5) 3.14 2) (check-within (approx-pi-n 111) pi .5) (check-within (approx-pi-n 1) 4.0 1) (check-within (approx-pi-n 6) 3.14 1) ; FUNCTION DEFINITION: (define approx-pi-n (lambda (n) (exact->inexact (cond ;base case: return 0 [(<= n 0) 0] ;recursive case 1: recall function with odd value of n [(even? n) (approx-pi-n (sub1 n))] [else (if (= (remainder n 4) 3) ;recursive case 2: call function and subtract 4/n term (- (approx-pi-n (- n 2)) (/ 4 n)) ;recursive case 3: add 4/n term and call function (+ (/ 4 n) (approx-pi-n (- n 2))))])))) ; POST-FUNCTION PRINTFS: (printf "(approx-pi-n 13) => ~a~%" (approx-pi-n 13)) (printf "(approx-pi-n 113) => ~a~%" (approx-pi-n 113)) (printf "(approx-pi-n 1113) => ~a~%" (approx-pi-n 1113)) (newline) (newline) ; ------------------------------- ; ; Define a TAIL-RECURSIVE function, called APPROX-PI-N-ACC, that ; consumes the following inputs: ; N, a positive odd integer ; ACC, a positive integer (accumulator) ; ; When given appropriate values for the inputs, it ; should return as its output the result of evaluating ; the sum ; ; 4 - 4/3 + 4/5 - 4/7 + 4/9 - ... (+/-) 4/n ; ; ===> Your output values should get closer to PI as n ; increases. ; ;;; ========================================================== ; CONTRACT: (approx-pi-n-acc pos-integer number) -> number ; HEADER: (define approx-pi-n-acc (lambda (n acc) ; PURPOSE: If acc = 0 initially, then output is equal to the sum ; 4 - 4/3 + 4/5 - 4/7 + ... (+/-)4/n ; which closely approximates PI for large enough n. ; ; PRE-FUNCTION TESTS: Need check-within instead of check-expect (check-within (approx-pi-n-acc 5 0) 3.14 2) (check-within (approx-pi-n-acc 111 0) pi .5) (check-within (approx-pi-n-acc 2 0) 4.0 .001) ;;; FUNCTION: (define approx-pi-n-acc (lambda (n acc) (exact->inexact (cond ;; base case, return acc [(<= n 0) acc] ;; rec case 1: n even, call func recursively with n-1 and same acc [(even? n) (approx-pi-n-acc (sub1 n) acc)] [else (if (= (remainder n 4) 3) ;; rec case 2: 4%n = 3, subtract 4/n term (approx-pi-n-acc (- n 2) (- acc (/ 4 n))) ;; rec case 3: 4%n = 1, add 4/n term (approx-pi-n-acc (- n 2) (+ acc (/ 4 n))))])))) ;;; ;;; POST-FUNCTION PRINTFS: (printf "(approx-pi-n-acc 5 0) -> ~a~%" (approx-pi-n-acc 5 0)) (printf "(approx-pi-n-acc 111 0) -> ~a~%" (approx-pi-n-acc 111 0)) (printf "(approx-pi-n-acc 2 0) -> ~a~%" (approx-pi-n-acc 2 0)) (newline) (newline) ; Define a wrapper function, approx-pi-n-acc-wrapper, that ; takes only one input, n. It should call approx-pi-n-acc ; with appropriately initialized input parameters. ; ; (pi-approx-n-acc-wrapper 1001) ===> 3.143588659585789 ; (pi-approx-n-acc-wrapper 10001) ===> 3.1417926135957908 ; CONTRACT: (approx-pi-n-acc-wrapper non-neg-integer) -> number ; HEADER: (define approx-pi-n-acc-wrapper (lambda (n) ...)) ; PURPOSE: Output is equal to the sum ; 4 - 4/3 + 4/5 - 4/7 + ... (+/-)4/n ; which closely approximates PI for large enough n. ; ; PRE-FUNCTION TESTS: Need check-within instead of check-expect (check-within (approx-pi-n-acc-wrapper 5) 3.14 2) (check-within (approx-pi-n-acc-wrapper 111) pi .5) (check-within (approx-pi-n-acc-wrapper 1) 4.0 .001) ; ;;;; FUNCTION: (define approx-pi-n-acc-wrapper (lambda (n) (approx-pi-n-acc n 0))) ;;; POST-FUNCTION PRINTFS: (printf "(approx-pi-n-acc-wrapper 50) -> ~a~%" (approx-pi-n-acc-wrapper 5)) (printf "(approx-pi-n-acc-wrapper 111) -> ~a~%" (approx-pi-n-acc-wrapper 111)) (printf "(approx-pi-n-acc-wrapper 1) -> ~a~%" (approx-pi-n-acc-wrapper 1)) (newline) (newline) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; The LOCAL special form: SPECIAL FORM 10 ; ; The local special form contains a "definition section" inside ; square braces. Within the square braces, you can write as ; many definitions as you want, for both constants and functions. ; ; (local ; [(define n1 ) ; (define n2 ) ; ... ; (define nk )] ; ) ; ; Where each can be any valid Racket expression, including ; a function definition. ; ; We will only use the local special form inside functions. ; You can view the local special form as setting up another ; local environment inside the local environment of the function ; that contains it. ; ; For example, the approx-pi function, given below, takes only ; one argument and produces the answer inside a local function. ; Instead of calling the approx-pi-n-acc, we'll write the equivalent ; of the accumulator function inside the wrapper (now called approx-pi). ; ; CONTRACT: (approx-pi non-neg-integer) -> number ; HEADER: (define approx-pi (lambda (n) ; PURPOSE: Output is equal to the sum ; 4 - 4/3 + 4/5 - 4/7 + ... (+/-)4/n ; which closely approximates PI for large enough n. ; PRE-FUNCTION TESTS: (check-within (approx-pi 5) 3.14 2) (check-within (approx-pi 111) pi .5) (check-within (approx-pi 1) 4.0 .001) (check-within (approx-pi 120) pi .1) ; FUNCTION DEFINITION: (define approx-pi (lambda (n) (local [(define approx-pi-acc (lambda (n acc) (exact->inexact (cond ;; base case, return acc [(<= n 0) acc] ;; rec case 1: n even, call func recursively with n-1 and same acc [(even? n) (approx-pi-acc (sub1 n) acc)] [else (if (= (remainder n 4) 3) ;; rec case 2: 4%n = 3, subtract 4/n term (approx-pi-acc (- n 2) (- acc (/ 4 n))) ;; rec case 3: 4%n = 1, add 4/n term (approx-pi-acc (- n 2) (+ acc (/ 4 n))))]))))] ;; Call helper function approx-pi-acc (approx-pi-acc n 0)))) ;;; POST-FUNCTION PRINTFS: (printf "(approx-pi 50) -> ~a~%" (approx-pi 5)) (printf "(approx-pi 111) -> ~a~%" (approx-pi 111)) (printf "(approx-pi 1) -> ~a~%" (approx-pi 1)) ;; Example 2: Write a version of the copy function that uses a ;; local special form that defines an internal function to compute ;; the answer (NOTE: this problem is from lecture 7) ;; Contract: (copy string integer) -> string ;; Header: (define copy (lambda (str n) ...)) ;; Purpose: produce string consisting of n copies of str. ;; Pre-function tests: (check-expect (copy "la" 4) "lalalala") (check-expect (copy "la" 1) "la") (check-expect (copy "do" 2) "dodo") (check-expect (copy "do" 0) "") ; Function definition: (define copy (lambda (str n) (local ;; define local accumulator function [(define copy-acc (lambda (n acc) (cond ;; base case: return acc when n = 0 [(= n 0) acc] ;; recursive case: append str to acc and recall function on n-1 [else (copy-acc (sub1 n) (string-append str acc))])))] ;; call inner accumulator function (copy-acc n "")))) ; Post-function printf's: (printf "(copy \"la\" 8) => ~a~%" (copy "la" 8)) (printf "(copy \"happy\" 4) => ~a~%" (copy "happy" 4))