Pascal's triangular-recursive triangle in the scheme

I recently started reading SICP , and I am very interested in converting a recursive procedure into a tail recursive form.

For "one-dimensional" situations (linear), such as a Fibonacci series or factorial calculations, it is not difficult to make a transformation.

For example, as the book says, we can rewrite the Fibonacci calculation as follows

(define (fib n)
    (fib-iter 1 0 n))
(define (fib-iter a b count)
    (if (= count 0) 
        b 
        (fib-iter (+ a b) a (- count 1))))

And this form is obviously tail-recursive

However, for a “two-dimensional” situation, such as calculating the Pascal triangle (example 1.12 in SICP), we can still easily write a recursive solution, for example, the following

(define (pascal x y) 
  (cond ((or (<= x 0) (<= y 0) (< x y )) 0)
        ((or (= 1 y) (= x y) ) 1)
        (else (+ (pascal (- x 1) y) (pascal (- x 1) (- y 1))))))

The question is how to convert this to a tail recursive form?

+4
3

, pascal ( , ) - :

(define (pascal x y) 
  (if (or (zero? y) (= x y))
      1
      (+ (pascal (sub1 x) y)
         (pascal (sub1 x) (sub1 y)))))

. , . , , , , . . Steven Skiena , 2- , . 278.

, , , ( ), , , - :

(define (pascal x y)
  (let ([table (make-vector (add1 x) 1)])
    (let outer ([i 1])
      (when (<= i x)
        (let inner ([j 1] [previous 1])
          (when (< j i)
            (let ([current (vector-ref table j)])
              (vector-set! table j (+ current previous))
              (inner (add1 j) current))))
        (outer (add1 i))))
    (vector-ref table y)))

, , . Racket :

(define (pascal x y)
  (define current null)
  (define previous null)
  (define table (make-vector (add1 x) 1))
  (for ([i (in-range 1 (add1 x))])
    (set! previous 1)
    (for ([j (in-range 1 i)])
      (set! current (vector-ref table j))
      (vector-set! table j (+ (vector-ref table j) previous))
      (set! previous current)))
  (vector-ref table y))

, . , Racket:

(define (pascal-triangle n)
  (for ([x (in-range 0 n)])
    (for ([y (in-range 0 (add1 x))])
      (printf "~a " (pascal x y)))
    (newline)))

(pascal-triangle 5)

1 
1 1 
1 2 1 
1 3 3 1 
1 4 6 4 1 
+3

: , O (), . , :

(define (pascal-on row col)
  (define (factorial from to acc)
    (if (> from to)
        acc
        (factorial (+ 1 from) to (* acc from))))

  (let* ((rmc (- row col))
         (fac-rmc (factorial 1 rmc 1))
         (fac-pos (factorial (+ rmc 1) col fac-rmc))
         (fac-row (factorial (+ col 1) row fac-pos)))
    (/ fac-row fac-pos fac-rmc)))

:

. , . , , , , , . .

(define (pascal row col)
  (define (aux tr tc prev acc)
    (cond ((> tr row) #f)              ; invalid input

          ((and (= col tc) (= row tr)) ; the next number is the answer
           (+ (car prev) (cadr prev))) 

          ((= tc tr)                   ; new row 
           (aux (+ tr 1) 1 (cons 1 acc) '(1)))

          (else (aux tr               ; new column
                     (+ tc 1) 
                     (cdr prev)
                     (cons (+ (car prev) (cadr prev)) acc))))) 

  (if (or (zero? col) (= col row))
      1
      (aux 2 1 '(1 1) '(1))))
+2

Óscar, :

;; Int Int (Int → Int) → Int
(define (pascal/k x y k)
  (cond
   [(or (<= x 0) (<= y 0) (< x y)) (k 0)]
   [(or (= 1 y) (= x y)) (k 1)]
   [else (pascal/k (- x 1) y
                   (λ (a)
                     (pascal/k (- x 1) (- y 1)
                               (λ (b) (k (+ a b))))))]))

;; Int Int → Int
(define (pascal x y) (pascal/k x y (λ (x) x)))

You can say that this program is not as satisfactory as the closure, which is "growing." But they stand out on a bunch. In the general case, the tail call point is not so much performance as security of space: you do not explode in the context of evaluation.

+1
source

All Articles