【问题标题】:Tail call optimization racket尾调用优化球拍
【发布时间】:2013-06-20 01:04:25
【问题描述】:

我正在尝试学习一些函数式编程,并且正在解决方案(球拍)中的项目欧拉问题以帮助我入门。我目前在problem 15 上,我认为我有一个正确的函数来计算晶格中的路径数。问题是对于大量的 gridSize 函数需要很长时间才能运行。

(define uniqueTraverse
  (lambda (x y gridSize)
    (cond
      ((and (eq? x gridSize) (eq? y gridSize)) 1)
      ((eq? x gridSize) (uniqueTraverse x (+ y 1) gridSize))
      ((eq? y gridSize) (uniqueTraverse (+ x 1) y gridSize))
      (else (+ (uniqueTraverse (+ x 1) y gridSize)
               (uniqueTraverse x (+ y 1) gridSize))))))

我试图弄清楚如何使这个函数尾调用递归,但我不知道该怎么做。我需要一些帮助来了解如何考虑使用尾调用优化来优化这样的函数。

【问题讨论】:

    标签: recursion functional-programming scheme racket


    【解决方案1】:

    问题在于您一遍又一遍地重新计算相同的结果。 为了解决这个问题,你不需要尾随 - 你需要记住旧的 结果并返回它们而不重新计算它们。这种技术称为记忆。

    这是一种解决方案:

    #lang racket
    
    (define old-results (make-hash))
    
    (define uniqueTraverse
      (lambda (x y gridSize)
        (define old-result (hash-ref old-results (list x y) 'unknown))
        (cond 
          ; if the result is unknown, compute and remember it
          [(eq? old-result 'unknown)
           (define new-result
             (cond
               ((and (eq? x gridSize) (eq? y gridSize)) 1)
               ((eq? x gridSize) (uniqueTraverse x (+ y 1) gridSize))
               ((eq? y gridSize) (uniqueTraverse (+ x 1) y gridSize))
               (else (+ (uniqueTraverse (+ x 1) y gridSize)
                        (uniqueTraverse x (+ y 1) gridSize)))))
           (hash-set! old-results (list x y) new-result)
           new-result]
          ; otherwise just return the old result
          [else old-result])))
    
    (uniqueTraverse 0 0 2)
    

    【讨论】:

      【解决方案2】:

      记忆是一种方式,另一种是使用不同的数据表示。

      我使用表示为矩阵或向量向量的网格。

      然后将顶行的值设置为 1(因为只有顶部边缘的路径。

      之后下一行的第一个为1,第二个为上列第一个条目的值,加上该行之前的条目或值,

      对行中的每个点进行递归,然后对每一行进行递归。

      那么答案就是递归完成后最后一行的最后一个点。

      对于 3x3 网格

      1 1 1
      1 2 3
      1 3 6
      

      6

      当键非常靠近时,(连续的或几乎连续的)向量表示将比散列更高效。

      (define (make-lattice-point-square n)
      (let ((lps (make-vector (+ n 1))))
       (let loop ((i 0))
        (if (> i n)
            lps
            (begin
                (vector-set! lps i (make-vector (+ n 1)))
                (loop (++ i)))))))
      
      (define (lattice-ref lat x y)
      ;; where x is row, y is column thought it's not really important
      (vector-ref (vector-ref lat y) x)) 
      
      (define (lattice-set! lat x y value)
       (vector-set! (vector-ref lat y) x value))
      
      ;; paths through a point are equal the the paths through the above point,
      ;; plus the paths through the left, those along the top and left edges 
      ;; only have one possible path through them
      
      (define (ways-exit-lattice n)
       (let ((lps (make-lattice-point-square n)))
        (letrec 
          ((helper 
            (lambda (x y)
          (if (or (= x 0) (= y 0))
                   (lattice-set! lps x y 1)
               (lattice-set! lps x y
                      (+ (lattice-ref lps (- x 1) y)
                    (lattice-ref lps x (- y 1)))))))
           (lattice-walker
            (lambda (x y)
            (cond ((and (= x n) (= y n)) 
                       (begin (helper x y) (lattice-ref lps x y)))
                      ((= y n) 
                       (begin 
                        (helper x y)
                        (lattice-walker (++ x) 0)))
                      (else 
                       (begin
                        (helper x y)
                        (lattice-walker x (++ y))))))))
         (lattice-walker 0 0))))  
      

      注意所有对 latice-walker 的调用都是尾调用。

      使用符合 RSR5 的方案

      【讨论】:

      • 按行工作,您可以将上一行和下一行混为一谈,因此您只需要一行,最初是 (n+1) 1s,即然后,您可以迭代 n 次,就地更新。
      猜你喜欢
      • 2017-06-10
      • 2012-10-07
      • 2011-03-31
      • 2014-04-06
      • 1970-01-01
      • 2011-07-11
      • 2015-04-23
      • 1970-01-01
      • 2011-02-22
      相关资源
      最近更新 更多