我有一个深度嵌套的列表,我想从列表中所有出现的给定元素中删除它。我有这段代码:
(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)
)
但是,它不会在内部级别删除。为什么??
最佳答案
我的猜测是您将向量用作序列。
(eliminate 3 [3 3])
;()
(eliminate 3 [3 [3]])
;([3])
如果您向我们展示了一个例子,这将很容易找到:啧啧!
发生了什么事?
虽然向量是可序列化的,但它们不是序列:
(seq? [])
;false
在外层,您将 lst
视为一个序列,因此 first
和 rest
起作用,因为它们将参数包装在一个隐含的序列
。但是 seq?
将在任何立即包含的向量上失败,甚至不会看到那些更远的向量。
如果将 seq?
替换为 sequential?
,列表和向量将起作用。
(sequential? [])
;true
正如@noisesmith 指出的,更严重的是您在内部范围内使用了def
和defn
。将它们替换为 let
或 letfn
。
您还可以改进您的风格:
- 将
(if-not (empty? lst) ... )
替换为(if (seq lst) ...)
。 - 使用
cond
来展平嵌套的if
。这需要反相 (1) 中的测试,因此不需要它。 - 在找到
value
的尾递归情况下使用recur
,如 @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)))
还有一个压痛点:
- 在知道
lst
不为空之前调用(first lst)
。不 造成的伤害:你只会得到nil
,你会忽略它。
使用解构的替代方法
你可以经常使用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))
关于clojure - 如何从嵌套列表中删除元素?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26175167/