从 Common-lisp 中的列表的任何级别删除所有出现的元素
Remove all occurences of elem from any level of a list in Common-lisp
我想用 mapcar/lambdas 解决这个问题。我知道如何定期这样做。到目前为止,我有类似的东西:
(defun removal (lista elem &optional final)
(cond
((and (atom lista) (eql lista elem)) nil)
((listp lista) (mapcan (lambda (e) ( removal e elem final)) lista))
(t (nconc final lista))))
出于某种原因,目前还没有 运行 但这是草稿。任何想法将 mapcar 放在哪里或如何摆脱可选列表 final?我需要使用映射函数或 lambda 和递归来解决这个问题。
添加了lambda和mapcan还是不能正常工作,根本不会构造列表
一些备注:
当您处理多级列表时,您通常是在处理树,不是吗?我会将 removal
和 lista
替换为 tree-remove
和 tree
。
如果 lista
是原子而不是数字怎么办?您最好在使用 =
之前检查 lista
是否为数字,或者接受 :test
参数。
你说 出于某种原因,它甚至不是 运行。你没有错误信息吗?它说什么? 未定义函数:lista?您在正常评估上下文中的列表的第一个位置使用 lista
符号。
为了避免使用可选参数,用labels
定义一个局部函数。这是另一个演示如何使用标签的示例:
(defun tree-occurences (tree number)
(let ((counter 0))
(labels ((count-occurences (form)
(typecase form
(sequence (map nil #'count-occurences form))
(number (when (= form number) (incf counter))))))
(count-occurences tree))
counter))
由于不清楚你有什么样的约束,这里有两个解决方案,第一个是递归的,没有高阶函数,第二个是有高阶函数的。
(defun remove-from-tree(el tree)
(cond ((null tree) nil)
((consp (car tree))
(cons (remove-from-tree el (car tree))
(remove-from-tree el (cdr tree))))
((eql (car tree) el) (remove-from-tree el (cdr tree)))
(t (cons (car tree) (remove-from-tree el (cdr tree))))))
在这个解决方案中有四种可能的情况 cond
:
- 如果树是空的,那么return是空树,
- 如果树的第一个元素是cons,也就是树,递归地把函数应用到原树的car和cdr上,cons结果得到新的树,
- (此时我们知道树的car是一个原子),如果car是
eql
到元素,则忽略car,继续递归函数到cdr,
- 否则,用汽车(它是一个不同于元素的原子)和将函数应用于树的cdr的结果构建一棵新树。
这是第二个版本:
(defun remove-from-tree(el tree)
(mapcan (lambda(subtree)
(cond ((null subtree) (list nil))
((consp subtree) (list (remove-from-tree el subtree)))
((eql subtree el) nil)
(t (list subtree))))
tree))
在本例中使用了泛函mapcan
,由于这是nconc
所有的结果,我们必须将list
添加到cond
的几个分支中内部函数。
应用于树的“顶部”元素的内部函数有四种情况:
- 如果子树为空,则值 returned 为
(list nil)
以便 mapcan
可以将此结果连接到其他结果,
- 如果子树是 cons,即树,则递归调用其上的函数和 return 结果列表,
- (此时我们知道子树是一个原子),如果等于要移除的元素,return
nil
(而这个不会出现在结果中mapcan
),
- 最后,return作为列表的原子,不同于元素。
您提到使用 mapcar 和 lambda,但 Common Lisp 还包括 mapcan,这在这里可能更有用。 mapcan 函数类似于 mapcar,但它不是创建函数结果列表,而是获取函数结果并将它们连接到列表中。例如,
(mapcan (lambda (x)
(list x x))
'(1 2 3))
;=> (1 1 2 2 3 3)
这对于过滤类型的任务很方便,因为您可以拥有映射到列表的功能总是 return 一个列表,如果它 returns nil,那么您实际上已经过滤掉了该元素。例如:
(mapcan (lambda (x)
(if (evenp x)
(list x) ; if even, return (x)
'())) ; if odd, return ()
'(1 2 3 4 5 6))
;;=> (2 4 6)
因此,您可以使用类似这样的方法从树的所有级别中删除一个元素。内部 %remove-all 函数执行实际工作,但它 始终 return 是一个列表,即使原始输入不是列表。所以,我们只需要从中提取第一个元素。
(defun remove-all (element tree &key (test #'eql))
(labels ((%remove-all (element tree)
(cond
((funcall test element tree) '())
((atom tree) (list tree))
(t (list (mapcan (lambda (child)
(%remove-all element child))
tree))))))
(car (%remove-all element tree))))
(remove-all 3 '(1 2 3 (3 2 1 (1 3 2))))
;;=> (1 2 (2 1 (1 2)))
(remove-all 3 5)
;;=> 5
(remove-all 3 3)
;;=> NIL
(remove-all 3 '(3))
;;=> NIL
我想用 mapcar/lambdas 解决这个问题。我知道如何定期这样做。到目前为止,我有类似的东西:
(defun removal (lista elem &optional final)
(cond
((and (atom lista) (eql lista elem)) nil)
((listp lista) (mapcan (lambda (e) ( removal e elem final)) lista))
(t (nconc final lista))))
出于某种原因,目前还没有 运行 但这是草稿。任何想法将 mapcar 放在哪里或如何摆脱可选列表 final?我需要使用映射函数或 lambda 和递归来解决这个问题。
添加了lambda和mapcan还是不能正常工作,根本不会构造列表
一些备注:
当您处理多级列表时,您通常是在处理树,不是吗?我会将
removal
和lista
替换为tree-remove
和tree
。如果
lista
是原子而不是数字怎么办?您最好在使用=
之前检查lista
是否为数字,或者接受:test
参数。你说 出于某种原因,它甚至不是 运行。你没有错误信息吗?它说什么? 未定义函数:lista?您在正常评估上下文中的列表的第一个位置使用
lista
符号。为了避免使用可选参数,用
labels
定义一个局部函数。这是另一个演示如何使用标签的示例:(defun tree-occurences (tree number) (let ((counter 0)) (labels ((count-occurences (form) (typecase form (sequence (map nil #'count-occurences form)) (number (when (= form number) (incf counter)))))) (count-occurences tree)) counter))
由于不清楚你有什么样的约束,这里有两个解决方案,第一个是递归的,没有高阶函数,第二个是有高阶函数的。
(defun remove-from-tree(el tree)
(cond ((null tree) nil)
((consp (car tree))
(cons (remove-from-tree el (car tree))
(remove-from-tree el (cdr tree))))
((eql (car tree) el) (remove-from-tree el (cdr tree)))
(t (cons (car tree) (remove-from-tree el (cdr tree))))))
在这个解决方案中有四种可能的情况 cond
:
- 如果树是空的,那么return是空树,
- 如果树的第一个元素是cons,也就是树,递归地把函数应用到原树的car和cdr上,cons结果得到新的树,
- (此时我们知道树的car是一个原子),如果car是
eql
到元素,则忽略car,继续递归函数到cdr, - 否则,用汽车(它是一个不同于元素的原子)和将函数应用于树的cdr的结果构建一棵新树。
这是第二个版本:
(defun remove-from-tree(el tree)
(mapcan (lambda(subtree)
(cond ((null subtree) (list nil))
((consp subtree) (list (remove-from-tree el subtree)))
((eql subtree el) nil)
(t (list subtree))))
tree))
在本例中使用了泛函mapcan
,由于这是nconc
所有的结果,我们必须将list
添加到cond
的几个分支中内部函数。
应用于树的“顶部”元素的内部函数有四种情况:
- 如果子树为空,则值 returned 为
(list nil)
以便mapcan
可以将此结果连接到其他结果, - 如果子树是 cons,即树,则递归调用其上的函数和 return 结果列表,
- (此时我们知道子树是一个原子),如果等于要移除的元素,return
nil
(而这个不会出现在结果中mapcan
), - 最后,return作为列表的原子,不同于元素。
您提到使用 mapcar 和 lambda,但 Common Lisp 还包括 mapcan,这在这里可能更有用。 mapcan 函数类似于 mapcar,但它不是创建函数结果列表,而是获取函数结果并将它们连接到列表中。例如,
(mapcan (lambda (x)
(list x x))
'(1 2 3))
;=> (1 1 2 2 3 3)
这对于过滤类型的任务很方便,因为您可以拥有映射到列表的功能总是 return 一个列表,如果它 returns nil,那么您实际上已经过滤掉了该元素。例如:
(mapcan (lambda (x)
(if (evenp x)
(list x) ; if even, return (x)
'())) ; if odd, return ()
'(1 2 3 4 5 6))
;;=> (2 4 6)
因此,您可以使用类似这样的方法从树的所有级别中删除一个元素。内部 %remove-all 函数执行实际工作,但它 始终 return 是一个列表,即使原始输入不是列表。所以,我们只需要从中提取第一个元素。
(defun remove-all (element tree &key (test #'eql))
(labels ((%remove-all (element tree)
(cond
((funcall test element tree) '())
((atom tree) (list tree))
(t (list (mapcan (lambda (child)
(%remove-all element child))
tree))))))
(car (%remove-all element tree))))
(remove-all 3 '(1 2 3 (3 2 1 (1 3 2))))
;;=> (1 2 (2 1 (1 2)))
(remove-all 3 5)
;;=> 5
(remove-all 3 3)
;;=> NIL
(remove-all 3 '(3))
;;=> NIL