【问题标题】:I need help to understand a lisp program that finds the depth of a list我需要帮助来理解一个查找列表深度的 lisp 程序
【发布时间】:2021-08-24 02:45:44
【问题描述】:

我需要帮助来从理论上理解我的代码。这是我的 lisp 程序:

(defun depth (lst)

  (if (or (null lst) (atom lst)) 0 

    (+ 1 (apply 'max (mapcar #'depth lst)))

  ))

我知道它适用于这个例子:

(write (depth '((a (b c) d r ((t))))) -> 3

我只是无法理解我尝试过的IF 语句的else 语句。

如果您能帮助我,将不胜感激。提前谢谢你。

【问题讨论】:

    标签: list recursion lisp common-lisp map-function


    【解决方案1】:

    这是您的代码,稍微重新格式化:

    (defun depth (value)
      (if (or (null value) (atom value))
          0
          (+ 1 (apply 'max (mapcar #'depth value)))))
    

    我将lst(顺便说一下,你可以写成list)重命名为value,因为这个名字让人困惑,因为它暗示变量总是一个列表,这是不正确的。函数depth 可以在任何值上调用:

    (depth "hello")
    => 0
    
    (depth 100)
    => 0
    

    if 的 then 分支在 value 为 NIL 或任何 atom 时进行评估。由于NIL 也是atom,因此测试表达式可以简化为(atom value)。当值是原子时,深度为零。

    if 的 else 分支在value 不是原子时进行评估,其中atom 的by definition 表示value 这里是cons。该函数还假定它是一个正确的列表,而不是某个循环列表。

    由于value 是该分支中的一个列表,我们可以在其上调用mapcar:(mapcar #'depth value);这是函数假定列表正确的地方。 这将为value 中的每个v 计算(depth v)。更准确地说,如果value 是一个长度为n 的列表,那么对mapcar 的调用计算为一个数字列表(D1 ... Dn),其中Di 是(depth Vi) 之间的所有i 1 和n。

    所以我们知道(apply 'max (mapcar ...)) 是(apply 'max depths) 对于一些数字列表depths。一般来说:

    (apply fn v1 ... vn list)
    

    ... 是一种调用由fn 表达式表示的函数对象的方法,其中至少包含n 元素(v1 到vn),以及存储在@ 中的任意数量的附加元素987654362@。当您引用函数时,如 'max 或编写 #'max 时,您在函数命名空间中通过函数名称来引用函数。

    将此与调用函数的通常方式进行对比:

    (f x y z)
    

    函数名称和传递的参数数量是固定的:只要读取表单,我们就知道有一个带有 3 个参数的 f 调用。

    apply 函数是一个内置函数,允许您在最后一个调用参数中传递列表中的其他参数。上面的调用可以写成:

    (apply #'f x y z ()) ;; note the empty list as a last argument
    

    也可以这样写:

    (apply #'f (list x y z)) ;; all arguments in the last list
    

    唯一的区别可能是运行时效率的问题(并且使用好的编译器,可能没有区别)。

    在你的例子中,你这样做:

    (apply max depths)
    

    这和写(伪代码)一样:

    (max d1 d2 d3 ... dn)
    

    ... 其中depths 是列表(list d1 d2 ... dn)。 但是我们不能直接把它们全部写出来,因为列表的内容只有在运行时才知道。

    因此,对应用的调用计算递归计算的所有深度中的最大深度。请注意,上面对apply 的使用有些不当,因为不应使用任意大小的列表调用apply:在名为CALL-ARGUMENTS-LIMIT 的标准中有一个限制,理论上允许低至50 ,这样一个列表的最大大小(我们将在下面看到一个替代方案)。

    最后,depth 会根据这个结果评估 (+ 1 ...)。换句话说,整个表达式可以概括为:列表的深度是其所有元素的最大深度加1。

    使用reduce

    您可以使用REDUCE 在列表上连续​​计算max,而不是apply。这比apply 更可取,因为:

    • 元素数量没有限制,比如apply

      (reduce 'max depths) ;; works the same, but more reliably
      
    • 不需要建立一个中间的深度列表,你可以遍历值列表,调用depth 并直接使用结果来计算最大值。骨架是:

      (reduce (lambda (max-so-far item) ...)
              value
              :initial-value 0)
      

    声明式方法

    代替reduce,loop 宏可以用作表达相同计算的更易读的替代方案。我还使用了typecase,我认为这使意图更清晰:

    (defun depth (value)
      (typecase value
        (atom 0)
        (cons (1+ (loop for v in value maximize (depth v))))))
    

    【讨论】:

      猜你喜欢
      • 2021-01-13
      • 1970-01-01
      • 2020-08-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-05-14
      • 1970-01-01
      相关资源
      最近更新 更多