我试图弄清楚如何访问LISP中嵌套列表中的元素。例如:
(p((3(17)(((5))))4)如果我使用dolist,我会遇到括号。有没有方法可以从列表中获取元素?
答案 0 :(得分:2)
这实际上是一个令人惊讶的微妙问题!它在某种程度上等同于询问:如何通过指定模式来获取HTML DOM的嵌套元素。 (稍后将详细介绍)
如果您只想将非列表元素作为序列,例如
((3 (1 7) (((5)))) 4) -->
(nth 3 (3 1 7 5 4)) -->
5
您可以使用'作弊'方式:CL亚历山大图书馆中的flatten
函数。 (通过quicklisp)
(ql:quicklisp :alexandria)
(nth 3 (alexandria:flatten '((3 (1 7) (((5)))) 4)))
这给了我们追捧,
5
但是,亚历山大的功能很简单,我们可以看一下源代码本身:
(defun flatten (list)
(cond
((null list)
nil)
((consp (car list))
(nconc (flatten (car list)) (flatten (cdr list))))
((null (cdr list))
(cons (car list) nil))
(t
(cons (car list) (flatten (cdr list))))))
正如你所看到的,它是一个递归函数 - 在每个递归级别它都会问一个问题:我正在展平的对象是什么?如果是空列表,则返回nil。我受够了!
否则它必须是非空列表。如果列表的第一个元素也是一个列表,那么将 压平,并压平函数参数列表的cdr
并连接结果。
如果第一个元素不是列表而第二个元素是'()
,那么这必然意味着我们有一个包含一个元素的列表:返回它。
最后的案例,耗尽了我们的可能性,列表中的第一个元素是一个原子,而列表的其余部分是一个至少包含一个元素的列表。在这种情况下,将第一个元素与在列表的其余部分上执行的flatten
的结果连接起来。
英语中的描述如此笨重的事实表明了递归的力量,(以及我自己在描述它时缺乏流畅性)。
但实际上你的问题可以解释为另一种方式:如果我的列表看起来像这样:((n1 (n2 n3) (((n4)))) n5)
我如何得到n2
,即使n2
本身就是一个列表?我们以前的递归算法不起作用 - 它取决于n2不是一个知道何时停止的列表。但是,我们可以仍然使用递归和我们搜索的列表作为模式的基础:
;; For each element in our pattern list look for a corresponding
;; element in the target, recursing on lists and skipping atoms that
;; don't match.
(defun extract-from-list (pattern target to-find)
(dotimes (i (length pattern))
(let ((ith-pattern (nth i pattern)))
(cond
((consp ith-pattern)
(let ((results
(extract-from-list (nth i pattern)
(nth i target)
to-find)))
(when results
(return results))))
(t
(if (eq to-find ith-pattern)
(return (nth i target))))))))
请注意,
(extract-from-list
'((n1 (n2 n3) (((n4)))) n5) ;; The pattern describing the list.
'((3 (1 7) (((5)))) 4) ;; The list itself.
'n4) ;; which of the elements we want.
仍然会回复旧答案:
5
但是,
(extract-from-list
'((n1 (n2 n3) (n4)) n5) ;; The pattern describing the list, note (n4) rather than (((n4)))
'((3 (1 7) (((5)))) 4) ;; The list itself.
'n4) ;; The element we want to pull from the list
返回
((5))
魔术! Lisp的一个方面使它非常强大。