2017-05-08 21 views
0

我试图写有易复发,一旦遇到重复切割序列的功能([1 2 3 1 4]应该返回[1 2 3]),这是我的函数:的Clojure - 包含?,连词和复发

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

第一个问题是contains?引发异常,我试图用some替换它,但没有成功。第二个问题是在recur部分,这也将抛出一个异常

+0

'包含?'检查,如果钥匙在例如,一套或地图。它也可以用于数组/向量来检查_index_是否存在。它在seqs上不受支持。如果列表是空的,'head'还有'a-seq'(第一个)的值,而'empty?'用于检查。 – cfrick

回答

3

你做了几个错误:

  • 你在序列中使用contains?。它仅适用于关联 集合。改为使用some
  • 对于empty?,您已经测试了序列的第一个元素(head)。 测试整个序列。
  • 使用向量来积累答案。 conj将元素添加到列表的前面 ,颠倒了答案。

纠正这些,我们得到

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

以上的作品,但它是缓慢的,因为它会扫描所有的元素缺席整个序列。所以最好使用一套。

我们称之为功能take-distinct,因为它与take-while类似。如果我们遵循的先例,并使其懒惰,我们可以这样做:

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

我们得到了有限序列的预计业绩:

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

我们可以拿那么多,我们从需要无尽的结果:

(take 10 (take-distinct (range))) 
=> (0 1 2 3 4 5 6 7 8 9) 

我会打电话给你的渴望版本take-distinctv,在map - >mapv先例。我会做这样说:

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

请注意,我们携带看到元素两次:

  • 作为载体,返回的解决方案;和
  • 作为一个集合,以测试成员身份。

三个失误的两个是由@cfrick评论。

0

保存一两条线,并尽可能明确地使逻辑&明确。为了尽可能它明显,我会做这样的事情:

(defn cut-at-repetition 
    [values] 
    (loop [remaining-values values 
     result   []] 
    (if (empty? remaining-values) 
     result 
     (let [found-values (into #{} result) 
      new-value (first remaining-values)] 
     (if (contains? found-values new-value) 
      result 
      (recur 
      (rest remaining-values) 
      (conj result new-value))))))) 

(cut-at-repetition [1 2 3 1 4]) => [1 2 3] 

此外,一定要书签The Clojure Cheatsheet,始终保持一个浏览器标签页中打开它。

+1

每次迭代构建一个全新的集合都非常昂贵,并且增量式构建它也同样容易。 – amalloy

+0

是的,但是因为它是出于教学目的,所以我想避免在'loop-recur'中同时存在'result'和'found-values'。 –

+0

...那么你不妨测试一下你的元素,而不是累积一组来测试。 – Thumbnail

0

我想听听关于这一点我写了自己这个效用函数反馈(使用filter有状态pred代替loop)的filter

(defn my-distinct 
    "Returns distinct values from a seq, as defined by id-getter." 
    [id-getter coll] 
    (let [seen-ids (volatile! #{}) 
     seen? (fn [id] (if-not (contains? @seen-ids id) 
          (vswap! seen-ids conj id)))] 
    (filter (comp seen? id-getter) coll))) 

(my-distinct identity "abracadabra") 
; (\a \b \r \c \d) 

(->> (for [i (range 50)] {:id (mod (* i i) 21) :value i}) 
    (my-distinct :id) 
    pprint) 

; ({:id 0, :value 0} 
; {:id 1, :value 1} 
; {:id 4, :value 2} 
; {:id 9, :value 3} 
; {:id 16, :value 4} 
; {:id 15, :value 6} 
; {:id 7, :value 7} 
; {:id 18, :value 9}) 

文档称“预解码必须是自由的一面 - 效果“,但我不确定在这种情况下是否可以。 filter是否保证按顺序遍历序列,而不是例如向前跳过?

+0

我认为'过滤器'要'pred'没有副作用,以防止块状物引起意外。 – Thumbnail

+0

我也这么想过。如果'过滤器'碰巧使用多线程,那么这种构造本质上是危险的。 :在这种情况下'atom'会更合适。 – NikoNyrh