【问题标题】:Remove all occurences of elem from any level of a list in Common-lisp从 Common-lisp 中列表的任何级别中删除所有出现的元素
【发布时间】:2016-03-23 10:18:13
【问题描述】:

我想使用 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))))

由于某种原因,它甚至没有运行到现在,但这是一个草稿。任何想法将地图车放在哪里或如何摆脱可选的列表决赛?我需要使用映射函数或 lambdas 和递归来解决这个问题。

添加了 lambda 和 mapcan 后仍然无法正常工作,它根本不会构造列表

【问题讨论】:

标签: list lisp common-lisp removeall


【解决方案1】:

一些备注:

  • 当您处理多级列表时,您通常使用的是树,不是吗?我会将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))
    

【讨论】:

  • 如果它是原子而不是元素,我认为它会进入真实状态,但我不知道它应该在那里做什么。是的,错误信息在地图车中,它说 List 不是一个函数。我不知道如何使用标签
  • 我的意思是...我不知道标签,所以我不应该使用它们。我想以某种方式使用地图函数/ lambda 解决这个问题
  • @Melye77 问题是:一个 (atom x) 正好意味着 (not (consp x)),所以一个数字就是一个原子。 但是,许多其他东西也是原子(例如字符串)。由于= 函数只为数字定义(请阅读spec,它并不复杂),非数字原子会出错。我为labels 添加了一个示例。
  • 好吧,所以我需要使用 eql 而不是 =。因为我确实希望它也可以与字符一起使用
  • @Melye77 如果你无权使用labels,只需定义一个辅助函数,例如rec-removalremoval-helper。是的,你应该使用eql
【解决方案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. 如果树的第一个元素是cons,即是一棵树,则将函数递归地应用于car和原树的cdr,并cons结果得到新树,
  3. (此时我们知道树的汽车是一个原子),如果汽车对元素是eql,那么忽略汽车,继续递归地把函数应用到cdr,
  4. 否则,使用汽车(它是与元素不同的原子)和将函数应用于树的 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的几个分支中。

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

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

【讨论】:

    【解决方案3】:

    您提到使用 ma​​pcarlambda,但 Common Lisp 还包括 mapcan,这在这里可能更有用。 ma​​pcan 函数类似于 mapcar,但它不是创建函数结果的列表,而是获取函数结果并将它们连接到列表中。例如,

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

    这对于过滤类型任务很方便,因为你可以让你在列表上映射的函数总是返回一个列表,如果它返回 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 函数执行实际工作,但它总是返回一个列表,即使原始输入不是一个列表。所以,我们只需要在之后从中提取第一个元素。

    (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
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-03-27
      • 1970-01-01
      • 2023-04-05
      • 1970-01-01
      • 2017-04-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多