【问题标题】:How can I make my average function tail recursive in Lisp如何在 Lisp 中使我的平均函数尾递归
【发布时间】:2019-11-08 23:29:11
【问题描述】:

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

  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-s;它们旨在成为顶级定义构造。尽管有嵌套,sumup 名称仍会输入到全局环境中。本地函数是使用fletlabels 创建的。
  • 是的,我的教授给我的另一个注意事项是不要在另一个函数中使用 defun

标签: recursion lisp common-lisp tail-recursion


【解决方案1】:

这就是我现在所拥有的

    (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

【讨论】:

  • 缩进看起来很奇怪
【解决方案2】:
(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)))

【讨论】:

    【解决方案3】:
    (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 函数时,缩进会渗入下一行。在第一个函数中,您可以说如果列表为 null(这是递归函数的基本情况),您可以将总和除以列表的长度。如果它不为null,您显然可以传递列表的cdr,通过将其增加列表的car,将其总和,然后将列表的长度增加一。通常使用 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 表达式也可以工作,但是因为它们没有名称,所以尾递归是不可能的,如果尾递归是强制性的,这使得标签特殊运算符对局部函数很有用。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-06-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-09-20
      • 2011-05-03
      • 2018-09-03
      • 2017-03-29
      相关资源
      最近更新 更多