我需要帮助来理解一个查找列表深度的 lisp 程序
I need help to understand a lisp program that finds the depth of a list
我需要帮助从理论上理解我的代码。这是我的 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
当 value
为 NIL 或任何 atom
时,计算 if
的 then 分支。由于 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)
对于 1 和 n
之间的所有 i
。
所以我们知道 (apply 'max (mapcar ...))
对于某些数字列表 depths
是 (apply 'max depths)
。总的来说:
(apply fn v1 ... vn list)
... 是一种调用由至少 n
个元素(v1
到 vn
)的 fn
表达式表示的函数对象的方法作为存储在 list
中的任意数量的附加元素。当您引用函数时,如 'max
,或者当您编写 #'max
时,您通过函数名称空间中的名称来引用函数。
将此与调用函数的通常方式进行对比:
(f x y z)
函数名称和传递的参数数量是固定的:一旦读取表格,我们就会知道有一个调用 f
的 3 个参数。
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
的不当使用,因为 apply
不应使用任意大小的列表调用:在名为 CALL-ARGUMENTS-LIMIT
的标准中有一个允许的限制理论上低至 50,这样的列表的最大大小(我们将在下面看到一个替代方案)。
最后,depth
对这个结果进行评估 (+ 1 ...)
。换句话说,整个表达式可以概括为:列表的深度是其所有元素的最大深度加1。
使用reduce
而不是 apply
,您可以使用 REDUCE
在列表上连续计算 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
宏可以用作表达相同计算的更具可读性的替代方法。我还使用 typecase
,在我看来这使意图更清晰:
(defun depth (value)
(typecase value
(atom 0)
(cons (1+ (loop for v in value maximize (depth v))))))
我需要帮助从理论上理解我的代码。这是我的 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
当 value
为 NIL 或任何 atom
时,计算 if
的 then 分支。由于 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)
对于 1 和 n
之间的所有 i
。
所以我们知道 (apply 'max (mapcar ...))
对于某些数字列表 depths
是 (apply 'max depths)
。总的来说:
(apply fn v1 ... vn list)
... 是一种调用由至少 n
个元素(v1
到 vn
)的 fn
表达式表示的函数对象的方法作为存储在 list
中的任意数量的附加元素。当您引用函数时,如 'max
,或者当您编写 #'max
时,您通过函数名称空间中的名称来引用函数。
将此与调用函数的通常方式进行对比:
(f x y z)
函数名称和传递的参数数量是固定的:一旦读取表格,我们就会知道有一个调用 f
的 3 个参数。
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
的不当使用,因为 apply
不应使用任意大小的列表调用:在名为 CALL-ARGUMENTS-LIMIT
的标准中有一个允许的限制理论上低至 50,这样的列表的最大大小(我们将在下面看到一个替代方案)。
最后,depth
对这个结果进行评估 (+ 1 ...)
。换句话说,整个表达式可以概括为:列表的深度是其所有元素的最大深度加1。
使用reduce
而不是 apply
,您可以使用 REDUCE
在列表上连续计算 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
宏可以用作表达相同计算的更具可读性的替代方法。我还使用 typecase
,在我看来这使意图更清晰:
(defun depth (value)
(typecase value
(atom 0)
(cons (1+ (loop for v in value maximize (depth v))))))