问题描述
我正在尝试编写带有recur的函数,以便在遇到重复时立即切断序列( [1 2 3 1 4]
应该返回 [1 2 3]
),这是我的功能:
I'm trying to write a function with recur that cut the sequence as soon as it encounters a repetition ([1 2 3 1 4]
should return [1 2 3]
), this is my function:
(defn cut-at-repetition [a-seq]
(loop[[head & tail] a-seq, coll '()]
(if (empty? head)
coll
(if (contains? coll head)
coll
(recur (rest tail) (conj coll head))))))
第一个问题是包含的容器?
引发异常,我尝试用 some替换它
,但没有成功。第二个问题是 recur
部分,这也会引发异常
The first problem is with the contains?
that throws an exception, I tried replacing it with some
but with no success. The second problem is in the recur
part which will also throw an exception
推荐答案
您犯了几个错误:
- 您使用过
包含吗?
在序列上。它仅适用于关联的
集合。使用some
代替。 - 您已经测试了序列的第一个元素(
head
)表示为空?
。
测试整个序列。 - 使用向量累积答案。
conj
将元素添加到列表的
前面,从而反转答案。
- You've used
contains?
on a sequence. It only works on associativecollections. Usesome
instead. - You've tested the first element of the sequence (
head
) forempty?
.Test the whole sequence. - Use a vector to accumulate the answer.
conj
adds elements to thefront of a list, reversing the answer.
更正这些,我们得到
(defn cut-at-repetition [a-seq]
(loop [[head & tail :as all] a-seq, coll []]
(if (empty? all)
coll
(if (some #(= head %) coll)
coll
(recur tail (conj coll head))))))
(cut-at-repetition [1 2 3 1 4])
=> [1 2 3]
以上方法有效,但这很慢,因为它会扫描每个缺少的元素的整个序列。所以最好使用一套。
The above works, but it's slow, since it scans the whole sequence for every absent element. So better use a set.
我们叫函数 take-distinct
,因为它类似于 take-while
。如果我们遵循该先例并使其变得懒惰,则可以这样做:
Let's call the function take-distinct
, since it is similar to take-while
. If we follow that precedent and make it lazy, we can do it thus:
(defn take-distinct [coll]
(letfn [(td [seen unseen]
(lazy-seq
(when-let [[x & xs] (seq unseen)]
(when-not (contains? seen x)
(cons x (td (conj seen x) xs))))))]
(td #{} coll)))
我们得到有限序列的预期结果:
We get the expected results for finite sequences:
(map (juxt identity take-distinct) [[] (range 5) [2 3 2]]
=> ([[] nil] [(0 1 2 3 4) (0 1 2 3 4)] [[2 3 2] (2 3)])
从无穷无尽的结果中获得的需求:
And we can take as much as we need from an endless result:
(take 10 (take-distinct (range)))
=> (0 1 2 3 4 5 6 7 8 9)
我会称呼您渴望的版本 take-distinctv
,在 map
-> mapv
上。 d这样做:
I would call your eager version take-distinctv
, on the map
-> mapv
precedent. And I'd do it this way:
(defn take-distinctv [coll]
(loop [seen-vec [], seen-set #{}, unseen coll]
(if-let [[x & xs] (seq unseen)]
(if (contains? seen-set x)
seen-vec
(recur (conj seen-vec x) (conj seen-set x) xs))
seen-vec)))
请注意,我们两次携带可见元素:
Notice that we carry the seen elements twice:
- 作为向量,返回作为解;
- 作为一组,以测试其成员资格。
@cfrick评论了三个错误中的两个。
Two of the three mistakes were commented on by @cfrick.
这篇关于clojure-包含?,conj和recur的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!