我怎样才能使我的平均函数在 Lisp 中尾部递归

How can I make my average function tail recursive in Lisp

我只是想让这个平均函数成为尾递归函数。我已经设法让我的功能发挥作用,这需要付出相当大的努力。之后我去问我的教授我的工作是否令人满意,他告诉我

  1. 我的 avg 函数不是尾递归的
  2. avg 没有为包含多个元素的列表生成正确的输出

过去 2 小时我一直在研究这段代码,但遇到了一些困难。任何人都可以帮助我确定我在这里不理解的内容。

与我的教授交谈,他很有帮助

    (defun avg (aList)
        (defun sumup (aList)
            (if (equal aList nil) 0
                ; if aList equals nil nothing to sum
                (+ (car aList) (sumup (cdr aList)) )
            )
        )

        (if 
            (equal aList nil) 0
            ; if aList equals nil length dosent matter
            (/ (sumup aList) (list-length aList) )
        )
    )

    (print (avg '(2 4 6 8 19))) ;39/5

我的测试预期结果在它之后立即发表评论 39/5

这就是我现在所拥有的

    (defun avg (aList &optional (sum 0) (length 0))

            (if aList 
            (avg (cdr aList) (+ sum (car aList))
            (+ length 1)) 
            (/ sum length)))

    (print (avg '(2 4 6 8 19))) ;39/5
(defun avg (list &optional (sum 0) (n 0))
   (cond ((null list) (/ sum n))
         (t (avg (cdr list) 
                 (+ sum (car list)) 
                 (+ 1 n)))))

这与:

(defun avg (list &optional (sum 0) (n 0))
   (if (null list) 
       (/ sum n)
       (avg (cdr list) 
            (+ sum (car list)) 
            (+ 1 n))))

或与您的写作更相似:

(defun avg (list &optional (sum 0) (n 0))
   (if list
       (avg (cdr list) 
            (+ sum (car list)) 
            (+ 1 n))
       (/ sum n)))
(defun avg (lst &optional (sum 0) (len 0))
   (if (null lst)
       (/ sum len)
       (avg (cdr lst) (incf sum (car lst)) (1+ len))))

您可以通过将整个 if-then/if-else 语句放在同一行来改进此处的缩进,因为在您的代码中,当您递归调用 avg 函数时,缩进会渗入下一行。在第一个函数中,您可以说如果列表为空(这是递归函数的基本情况),您可以将总和除以列表的长度。如果它不为空,显然可以传递列表的 cdr,通过将列表的 car 递增到目前为止的总和,然后将列表的长度递增 1。通常使用 incf 或 1+ 函数是不明智的,因为它们具有破坏性,但在这种情况下它们只会产生局部影响,因为它们只影响这个特定函数的可选 sum 和 len 参数,而不是结构原始列表(否则我会传递列表的副本)。

另一种选择是使用递归局部函数,并避免使用可选参数,并且不必在每次递归调用时都计算列表的长度。在您的原始代码中,您似乎试图在 avg 函数的上下文中使用局部函数,但您应该使用 "labels" 特殊运算符来执行此操作,而不是 "defun":

(defun avg (lst)
   (if (null lst)
       0
       (labels ((find-avg (lst sum len)
          (if (null lst)
              (/ sum len)
              (find-avg (cdr lst) (incf sum (car lst)) len))))
          (find-avg lst 0 (length lst))))

我不是 100% 确定你的教授是否希望局部函数是尾递归的,或者他指的是全局函数 (avg),但这就是你也可以使局部函数成为尾部函数的方法-如果这也是一种可接受的补救措施,则递归。它实际上在某些方面更有效,尽管它需要更多的代码行。在这种情况下,lambda 表达式也可以工作,但是因为它们没有名称,尾递归是不可能的,这使得标签 Special operator is useful for local functions if tail-recursion is mandatory.