【问题标题】:How to write a recursion in lisp?如何在 lisp 中编写递归?
【发布时间】:2019-10-03 13:56:27
【问题描述】:

我需要你的大力帮助。 我想在 Lisp 中编写一个名为 rec 的函数,该函数接收如下列表: (rec'(10 (12 (5 2 2) 9) (2 14 (4 (3 1 1) 5)))) 我的代码应该检查内部列表的总和是否等于外部列表的数量(5+2+2= 9 等于 9,然后我进一步 9+9+12 = 30, 3+3+1 = 5 等于 5,然后 5+5+4=14 等于 14,14+14+2=30 和 30 等于 30,至少我将 10 加到 30+30 和整个列表的总和是 70。如果我的总和等于数字,我将返回整个列表的总和,否则我将返回 Nil。我希望你明白我的意思……现在我想用递归解决它,我尝试了一些方法,但是我无法解决,希望有人能进一步帮助我...

(defun rec(list)

)

(rec '(10 (12 (5 2 2) 9) (2 14 (4 (3 1 1) 5))))


【问题讨论】:

    标签: list recursion lisp


    【解决方案1】:

    对我来说,这似乎唯一可行的方法是所有列表都有 3 个元素。让我们称之为三倍。

    (defun triplep (e)
      (and (listp e)
           (= 3 (length e)))
    
    ;; test
    (triplep 5)        ; ==> nil
    (triplep '(3 4 5)) ; ==> t
    

    我不喜欢 rec 这个名字,所以我将其命名为 sum-triple。我猜:

    (sum-triple 5) ; ==> 5
    (sum-triple '(3 1 1)) ; ==> 
    (let ((x2 (sum-triple 1))
          (x3 (sum-triple 1))
      (if (= x2 x3)
          (+ (sum-triple 3) x2 x3)
          0))
    

    现在要让它返回'(),你需要包装它。第一个调用需要与其他所有调用不同,以便您可以跳出帮助程序并在需要时做一些完全不同的事情:

    (defun my-fun (x)
      (labels ((helper (x acc) ; acc here is just to show you can hold state
                 ;; actual implementation that calls itself
                 ;; if it finds a bad value it can short cut with
                 (return-from my-fun '()))
        (helper x '())))
    

    【讨论】:

      【解决方案2】:

      如何在您明确什么需要完成后,将更容易实现该功能。

      输入

      您的输入是一个列表,但不是任何类型的列表。 让我们将这种特定类型的列表命名为 node

      一个节点要么是一个空列表,要么是一个由3个元素组成的列表(v n1 n2),其中:

      • v 是一个数字,
      • n1 (resp. n2) 要么是一个节点 要么是一个数字

      输出

      当您在节点上调用rec时,它应该输出一个数字nil

      大纲

      让我们定义一个辅助num 函数,它接受一个数字或一个节点并返回一个数字或零,通过调用rec

      • (num n) 的数字 n 应该返回 n
      • (num n) 对于节点 n 应该返回 (rec n)

      那么,rec 可以定义如下:

      • (rec nil) 应该是 nil
      • (num n1)(num n2) 是相等的数字时,(rec (v n1 n2)) 应该返回(+ v (num n1) (num n2))。在任何其他情况下,rec 应返回 nil

      示例

      以下是一种可能的实现方式,它依赖于本地函数定义 (FLET) 等运算符、基于值类型 (TYPECASE) 的切换和 提前返回 技术。另见DESTRUCTURING-BIND。使用逻辑运算符(和/或)对于组合可能的 NIL 中间结果很有用:

      (defun rec (node)
        (flet ((num-or-fail (n)
                 (or (typecase n
                       (number n)
                       (cons (rec n)))
                     (return-from rec))))
          (and node
               (destructuring-bind (v n1 n2) node
                 (let ((v1 (num-or-fail n1))
                       (v2 (num-or-fail n2)))
                   (and (= v1 v2)
                        (+ v v1 v2)))))))
      

      在 REPL(命令行)中,您可以为 rec 激活跟踪:

      CL-USER> (trace rec)
      

      然后你就可以测试了:

      CL-USER> (rec '(10 (12 (5 2 2) 9) (2 14 (4 (3 1 1) 5))))
      

      上面返回 70 并在 SBCL 中打印以下跟踪:

      0: (REC (10 (12 (5 2 2) 9) (2 14 (4 (3 1 1) 5))))
        1: (REC (12 (5 2 2) 9))
          2: (REC (5 2 2))
          2: REC returned 9
        1: REC returned 30
        1: (REC (2 14 (4 (3 1 1) 5)))
          2: (REC (4 (3 1 1) 5))
            3: (REC (3 1 1))
            3: REC returned 5
          2: REC returned 14
        1: REC returned 30
      0: REC returned 70
      

      Early return 甚至可以从最外层的调用中逃脱,因为它意味着整个结果为 NIL(有点像异常)。您也可以将 rec 设为本地函数,与 num-or-fail 相互递归,并以不同的方式命名您的主函数:

      (defun sumtree (node)
        (labels ((num-or-fail (n)
                   (or (typecase n
                         (number n)
                         (cons (rec n)))
                       (return-from sumtree)))
                 (rec (node)
                   (and node
                        (destructuring-bind (v n1 n2) node
                          (let ((v1 (num-or-fail n1))
                                (v2 (num-or-fail n2)))
                            (and (= v1 v2)
                                 (+ v v1 v2)))))))
          (rec node)))
      

      上面,当中间结果之一是nil时,return-from展开整个递归调用堆栈并直接返回nil

      【讨论】:

      • 请注意,该示例故意使用学生不应该知道的功能,不要只是复制它。
      猜你喜欢
      • 2023-03-28
      • 2019-04-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-08-15
      • 1970-01-01
      • 2020-04-02
      • 1970-01-01
      相关资源
      最近更新 更多