Recursion 如何使我的平均函数尾部在Lisp中递归

Recursion 如何使我的平均函数尾部在Lisp中递归,recursion,lisp,common-lisp,tail-recursion,Recursion,Lisp,Common Lisp,Tail Recursion,我只是想让这个平均函数成为尾部递归函数。我已设法使我的职能发挥作用,这需要付出相当大的努力。后来我去问教授我的工作是否令人满意,他告诉我 我的avg函数不是尾部递归函数 avg没有为包含多个元素的列表生成正确的输出 在过去的两个小时里,我一直在玩弄这段代码,结果遇到了一些麻烦。有人能帮我找出我不理解的地方吗 跟我的教授说他是有益的 (defun avg (aList) (defun sumup (aList) (if (equal aList ni

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

  • 我的avg函数不是尾部递归函数
  • avg没有为包含多个元素的列表生成正确的输出
  • 在过去的两个小时里,我一直在玩弄这段代码,结果遇到了一些麻烦。有人能帮我找出我不理解的地方吗

    跟我的教授说他是有益的

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

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


    我不能100%确定你的教授是否希望局部函数是尾部递归的,或者他指的是全局函数(avg),但是如果这也是一个可以接受的补救方法,那么你也可以通过这种方法使局部函数尾部递归。它实际上在某些方面更有效,尽管它需要更多的代码行。在这种情况下,lambda表达式也可以工作,但由于它们没有名称,因此不可能使用尾部递归,这使得标签特殊运算符对于局部函数非常有用,如果尾部递归是必需的

    不要嵌套
    defun
    -s;它们是一个顶级定义结构。不管嵌套情况如何,
    汇总
    名称都会输入到全局环境中。局部函数是使用
    flet
    标签创建的。是的,教授给我的另一个提示是不要在另一个函数中使用defun缩进看起来很奇怪
    
    (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))))
    
    (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))))