2014-10-03 96 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