2014-10-03 65 views
1

我有一个深度嵌套的列表,我想从列表中的所有事件中删除给定的元素。我有这样的代码:如何从嵌套列表中删除元素?

(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) 
      ) 

但是,它不会在内层删除。为什么??

+0

时,如果未能在什么输入?我试了几次,但我没有看到它失败。请在你的问题中包括一个例子。 – 2014-10-03 10:29:19

+0

将右括号放在一行上是[认为不好的样式](https://github.com/bbatsov/clojure-style-guide#gather-trailing-parens)(所有LISP都一样) – Sylwester 2014-10-03 13:01:49

+3

'def'和' defn'只能创建全局变量,使用'let' /'fn' /'letfn'作为本地绑定,'atom' /'ref' /'agent'用于全局变量状态为 – noisesmith 2014-10-03 13:20:20

回答

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))))))) 

这是从书“的小策士”,这是一个很好的来源,学习如何写这样的递归函数的简单递归函数。

让我们看看,如果我们可以把它翻译成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 ((((())))))) 
+0

非常感谢大家!我想到了。 :) – 2014-10-03 21:28:01

2

我的猜测是,你正在使用向量作为序列。

(eliminate 3 [3 3]) 
;() 

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

这本来是小事找到了你向我们展示一个例子:啧!


这是怎么回事?

虽然载体seqable,他们不是序列:

(seq? []) 
;false 

在外部级,你把lst作序,所以firstrest工作,因为他们包装他们的论点的隐含seq。但是seq?将在任何立即封闭的矢量上失败,并且甚至不会看到那些矢量。

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

(sequential? []) 
;true 

更为严重,因为@noisesmith指出的,是你在内部范围的defdefn使用。将它们替换为letletfn

你也可以提高你的风格:

  1. 更换(if-not (empty? lst) ...)(if (seq lst) ...)
  2. 使用cond来扁平化嵌套if s。这需要反转 (1)中的测试,因此不需要它。
  3. 使用recur为尾递归的情况下,你发现value,因为 @Mark呢。

如果你不希望看到的结果,现在看起来远:

(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
(defn expel [victim xs] 
    (mapcat (fn [x] 
      (cond 
       (sequential? x) [(expel victim x)] 
       (= x victim) [] 
       :else [x])) 
      xs)) 
+0

+1好的解决方案,虽然我会给它另一个名字。 – Mark 2014-10-07 04:51:15

+0

@Mark谢谢。我接受了使用不同名称的建议。 – overthink 2014-10-07 13:09:43