Search code examples
syntaxschemeracketpi

Calculating a series approximation for pi & scheme syntax


Im taking a run with scheme for the first time & am trying write a simple recursive function which will calculate pi using the Leibniz formula. The procedure takes in the accuracy needed and returns an appropriate pi approximation.

for example: make-pi 0.001 -> 3.14109...

Im getting caught up with the syntax however and am a lost in the syntax and proper way to call/return variables in scheme.

; Define count for number of loops, sum for pi estimate, and num/denom for calculating estimation in series
(define count 1.0)
(define sum 4.0)
(define denomenator 3.0)
(define numerator 4.0)

; make-pi gets accuracy needed as input ie (make-pi 0.001 => 3.1410926536210413
(define make-pi
  (lambda (accuracy)
    ; determine if next fraction will be + or - based on number of iterations thus far
    (if (zero? sign count)
        ; add fraction if odd, subtract otherwise
        (+ sum (/ numerator denomenator))
        (- sum (/ numerator denomenator)))
    (define (check-accuracy sum)
      ; use check accuracy with current sum
      (if (zero? check-accuracy)
          ;return sum if within needed accuracy
          sum
          ; update count and new denom values if not accurate enough
          ; loop back if sum is not accurate enough
          (begin
            (+ count 1)
            (+ denomenator 2)
            (make-pi (sum)))))))



; if interation is even return 1 else 0
(define sign
  (lambda (count)
    (if (even? count))
  1
  0))

; if pi - sum is within desired accuracy, return 1 else 0
(define check-sum
  lambda (sum)
  (define check-accuracy
    lambda (accuracy)

  (if ((- 3.14159265358979 sum) < (abs(accuracy))))
  1
  0))

Solution

  • How to call variables. Given that the symbol add evaluates to a procedure you call by enclosing them in parentheses:

    (add 1 2) ; returns 3 if `add` is like global variable `+`
    

    Basically it is the semantic eqvivalent to add(1, 2) in Algol languages. Be careful not to use extra parentheses since ((add 1 2)) is the same as add(1, 2)() etc.

    How to return a variable. Well you let it be the tail expression.

    (define (positive? n)
      (if (> 0 n)
          #t         ; tail that returns true 
          #f))       ; tail that returns false
    

    Actually what happens is that if either does the consequent or the alternative based on the test. SO the result of the whole if form becomes either true or false and the if also returns that value. Ultimately the last expression of the procedure which happen to be the if expression gets returned as result of positive?.

    You make a global variable with define

    (define my-value 10) ; my-value is 10
    

    And of course a procedure is a value:

    (define add1 (lambda (n) (+ n 1))) ; value of add1 is a procedure
    

    However (define sign (count)) will create the variable sign to be the result of calling the procedure count with no arguments. var sign = count();. (define check-accuracy lambda) is the same as var checkAccuracy = lambda;, probably not bound.

    (((- 3.14159265358979 sum) < (abs(accuracy))))) is the same as: sub( 3.14159265358979, sum)(some-variable-<, abs(accuracy()))()

    In Scheme you probably want (< (- 3.14159265358979 sum) (abs accuracy)) instead.

    A define (helper) inside a procedure needs to be at the beginning before the body of the procedure. eg.

    (define (fibonacci n)
      ; define a local helper procedure
      (define (helper n a b)
        (if (zero? n)
            a
            (helper (- n 1) b (+ a b))))
    
      ; use the helper
      (helper n 0 1))
    

    And since (define (proc arg ...) body ...) is the same as (define proc (lambda (arg ...) body ...)) you can write the same:

    (define fibonacci (lambda (n)
      ; define a local helper procedure
      (define helper (lambda (n a b)
        (if (zero? n)
            a
            (helper (- n 1) b (+ a b)))))
    
      ; use the helper
      (helper n 0 1)))
    

    Helpers that does not use the free variables can be global. They are easier to test on their own that way. You can move them inside as helpers later (or not)

    (sign 1) ; ==> is it 0?
    

    Remember to take time to learn the language. This is probably the second time you learn a new language and would be different than learning a new dialect of a language you already know.