我怎样才能使我的平均函数在 Lisp 中尾部递归
How can I make my average function tail recursive in Lisp
我只是想让这个平均函数成为尾递归函数。我已经设法让我的功能发挥作用,这需要付出相当大的努力。之后我去问我的教授我的工作是否令人满意,他告诉我
- 我的 avg 函数不是尾递归的
- 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.
我只是想让这个平均函数成为尾递归函数。我已经设法让我的功能发挥作用,这需要付出相当大的努力。之后我去问我的教授我的工作是否令人满意,他告诉我
- 我的 avg 函数不是尾递归的
- 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.