从Common-lisp中列表的任何级别删除elem的所有出现

时间:2015-12-17 03:42:50

标签: list lisp common-lisp removeall

我想使用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或如何摆脱可选列表最终的想法?我需要使用map函数或lambdas和递归来解决这个问题。

即使添加了lambda和mapcan之后仍然无法正常工作,它根本无法构建列表

3 个答案:

答案 0 :(得分:2)

一些评论:

  • 当您处理多个级别的列表时,您通常使用树,不是吗?我会将removallista替换为tree-removetree

  • 如果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))
    

答案 1 :(得分:2)

由于不清楚你有什么样的约束,这里有两个解决方案,第一个是递归的,没有高阶函数,第二个是更高阶函数。

(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中有四种可能的情况:

  1. 如果树是空的,则返回一棵空树,
  2. 如果树的第一个元素是一个缺点,即树,则递归地将该函数应用于原始树的汽车和cdr,并将结果合并以获得新树,
  3. (此时我们知道树的汽车是一个原子),如果汽车是元素的eql,那么忽略汽车并继续递归地将函数应用到cdr,
  4. 否则,用汽车构建一个新树(这是一个与元素不同的原子)以及将函数应用于树的cdr的结果。
  5. 这是第二个版本:

    (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的几个分支中

    应用于树的“顶部”元素的内部函数有四种情况:

    1. 如果子树为空,则返回的值为(list nil),以便mapcan可以将此结果与其他结果连接,
    2. 如果子树是一个缺点,那就是一棵树,递归调用它上面的函数并返回结果列表,
    3. (此时我们知道子树是一个原子),如果它等于要删除的元素,则返回nil(这不会出现在mapcan的结果中),
    4. 最后,返回与元素不同的原子作为列表。

答案 2 :(得分:2)

您提到使用 mapcar lambda ,但Common Lisp还包含mapcan,这可能会更有用。 mapcan 函数类似于mapcar,但它不是创建函数结果列表,而是获取函数结果并将它们连接成一个列表。例如,

(mapcan (lambda (x)
          (list x x))
        '(1 2 3))
;=> (1 1 2 2 3 3)

这对于过滤类型任务很方便,因为您可以让您在列表上进行映射的功能始终返回一个列表,如果它返回 nil ,那么您可以使用该功能。基本上过滤掉了那个元素。 E.g:

(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 功能可以完成实际工作,但始终会返回一个列表,即使原始输入不是列表。所以,我们只需要从中提取第一个元素。

(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