如何从嵌套列表中删除元素?

时间:2014-10-03 07:47:50

标签: clojure

我有一个深度嵌套的列表,我想删除列表中出现的所有元素。我有这段代码:

(defn eliminate [value lst]
            (defn sub-eliminate [lst]
              (def currentItem (first lst))
              (if-not (empty? lst)
                (if (seq? currentItem)
                  (cons (sub-eliminate currentItem) (sub-eliminate (rest lst)))
                  (if (= value currentItem)
                    (sub-eliminate (rest lst))
                    (cons currentItem (sub-eliminate (rest lst)))
                    )
                  )
                '()
                )
              )
              (sub-eliminate lst)
            )

但是,它并没有在内层删除。为什么?

3 个答案:

答案 0 :(得分:2)

我的猜测是你使用矢量作为序列。

(eliminate 3 [3 3])
;()

(eliminate 3 [3 [3]])
;([3])

如果你向我们展示了一个例子,那将是微不足道的:tut,tut!


发生了什么事?

虽然矢量是可选的,但它们不是序列:

(seq? [])
;false

在外层,您将lst视为序列,因此firstrest可以正常工作,因为它们将参数包装在隐式seq中。但是seq?将在任何立即封闭的向量上失败,并且甚至不会看到那些向内的向量。

如果您将seq?替换为sequential?,则列表和向量将起作用。

(sequential? [])
;true

正如@noisesmith指出的那样,更严重的是你在内部范围内使用defdefn。将其替换为letletfn

你也可以改善你的风格:

  1. (if-not (empty? lst) ... )替换为(if (seq lst) ...)
  2. 使用cond展平您的嵌套if。这需要反转 (1)中的测试,因此不需要它。
  3. 使用recur作为尾部递归情况,找到value,如 @Mark确实如此。
  4. 如果您不想看到结果,请立即离开:

    (defn eliminate [value lst]
      (letfn [(sub-eliminate [lst]
                (let [current-item (first lst)]
                  (cond
                   (empty? lst) '()
                   (sequential? current-item) (cons (sub-eliminate current-item) 
                                                    (sub-eliminate (rest lst)))
                   (= value current-item) (recur (rest lst))
                   :else (cons current-item (sub-eliminate (rest lst))))))]
        (sub-eliminate lst)))
    

    还有一个招标点:

    • 在您知道(first lst)不为空之前,您会调用lst。没有 伤害已经完成:你只会得到nil,你忽略了。

    使用解构的替代Apporach

    您经常可以使用destructuring来缩短序列的递归处理。我倾向于表达你的功能:

    (defn eliminate [x xs]
      ((fn rem-x [xs]
         (if-let [[y & ys] (seq xs)]
           (if (= x y)
             (recur ys)
             (cons
              (if (sequential? y) (rem-x y) y)
              (rem-x ys)))
           ()))
       xs))
    

答案 1 :(得分:1)

为了学习,请看一下这个功能:

(define rember*
  (lambda (x l)
    (cond ((null? l) '())
          ((atom? (car l))
          (if (eq? (car l) x)
              (rember* x (cdr l))
              (cons (car l)
                    (rember* x (cdr l)))))
          (else (cons (rember* x (car l))
                      (rember* x (cdr l)))))))

这是一本关于“The Little Schemer”的简单递归函数,它是学习如何编写这类递归函数的好资源。

让我们看看我们是否可以将其翻译成Clojure:

(defn rember* [x l]
  (cond (empty? l) '()
        (seq? (first l)) (cons (rember* x (first l))
                               (rember* x (rest l)))
        :else (if (= (first l) x)
                (recur x (rest l))
                (cons (first l)
                      (rember* x (rest l))))))

user> (rember* 'x '((x y) x (z (((((x))))))))
;; => ((y) (z ((((()))))))

答案 2 :(得分:1)

(defn expel [victim xs] 
  (mapcat (fn [x]
            (cond
              (sequential? x) [(expel victim x)]
              (= x victim) [] 
              :else [x])) 
          xs))