简体   繁体   English

尾调用优化球拍

[英]Tail call optimization racket

I'm trying to learn some functional programming and am doing project euler problems in scheme (racket) to get me started. 我正在尝试学习一些函数式编程,并在计划(球拍)中进行项目euler问题以便让我开始。 I'm currently on problem 15 and I think I have a correct function for computing the number of paths in the lattice. 我目前正处理问题15 ,我认为我有一个正确的函数来计算晶格中的路径数。 Problem is that for large number of gridSize the function takes very long time to run. 问题是对于大量的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))))))

I'm trying to figure out how to make this function tail call recursive but I don't know how to do it. 我试图找出如何使这个函数尾调用递归,但我不知道如何做到这一点。 I need some help getting started on how to think about optimizing functions like this using tail call optimization. 我需要一些帮助,开始考虑如何使用尾调用优化来优化这样的函数。

The problem is that you recompute the same results over and over again. 问题是你一遍又一遍地重新计算相同的结果。 To solve this, you don't need tail calls - you need to remember old results and return them without recomputing them. 要解决这个问题,您不需要尾调用 - 您需要记住旧结果并返回它们而不重新计算它们。 This technique is called memoization. 这种技术称为memoization。

This is one solution: 这是一个解决方案:

#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)

Memoization is one way, another is to use a different data representation. 记忆是一种方式,另一种是使用不同的数据表示。

I used the grid represented as a matrix, or vector of vectors. 我使用表示为矩阵的网格或向量的向量。

Then set the value of the top row to 1 (as there is only on path on the top edge. 然后将顶行的值设置为1(因为只有顶部边缘上的路径。

After that the next row ther first of the row is one, the second is the value of the entry in the column one above, plus the entry of or value before it in the row, 在那之后,行的第一行是第一行,第二行是上面第一列中的条目的值,加上行中的条目或值,

Recurse for each of the points in the row, and then for each row. 递归该行中的每个点,然后递归每一行。

The answer then is the last point in the last row when you are done recursing. 然后答案是完成递归后最后一行的最后一点。

For a 3x3 grid 对于3x3网格

1 1 1
1 2 3
1 3 6

6 6

Where the keys are very close together, (continuous, or nearly so) a vector representation is going to be more performant than a hash. 在密钥非常靠近的情况下(连续或几乎如此),向量表示将比哈希更具性能。

(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))))  

notice all the calls to latice-walker are tail calls. 注意所有对latice-walker的调用都是尾调用。

using RSR5 compliant scheme 使用RSR5兼容方案

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM