我需要帮助来理解一个找到列表深度的 lisp 程序 使用 depth声明式方法

问题描述

我需要帮助从理论上理解我的代码。这是我的 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 语句。

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

解决方法

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

(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 为 NIL 或任何 value 时,会评估 atomthen 分支。由于 NIL 也是 atom,测试表达式可以简化为 (atom value)。当 value 是一个原子时,深度为零。

if 不是原子时评估 valueelse 分支,atomby definition 表示 {{1} } 这里是一个value。该函数还假定它是一个适当的列表,而不是某个循环列表。

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

所以我们知道 i 对于某些数字列表 n(apply 'max (mapcar ...))。一般:

(apply 'max depths)

... 是一种调用由 depths 表达式表示的函数对象的方法,该表达式至少包含 (apply fn v1 ... vn list) 个元素(fnn),以及v1 中存储的任意数量的附加元素。当您引用函数时,如 vn,或者当您编写 list 时,您在函数命名空间中通过名称引用函数。

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

'max

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

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

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) 。 但是我们不能直接把它们全部写出来,因为列表的内容只有在运行时才知道。

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

最后,apply 根据此结果计算 apply。换句话说,整个表达式可以概括为:列表的深度是其所有元素的最大深度加1。

使用 depth

代替 (+ 1 ...),您可以使用 REDUCE 在列表上连续​​计算 reduce。这比 apply 更可取,因为:

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

    apply
  • 不需要构建深度的中间列表,您遍历值列表,调用 apply 并直接使用结果来计算最大值。骨架是:

    (reduce 'max depths) ;; works the same,but more reliably
    

声明式方法

代替 depth(reduce (lambda (max-so-far item) ...) value :initial-value 0) 宏可以用作更易读的替代方法来表达相同的计算。我还使用了 reduce,我认为这使意图更清晰:

loop