7

我有一系列这样的地图对(目前大约 17000 对)

(def myseq '({:name "Peter" :rank 2222} {:name "Anna" :rank 111}))

我想将特定对过滤成一个新序列

(filter (fn [x] (> x 222)) (:rank (first myseq)))

我一直在尝试使用这样的循环进行迭代,但不断遇到线程死亡。此外,如果我在单个地图集合上使用过滤器,它只会返回一个新序列,不确定我是否需要自己在这里创建一个?

(defn remove-lower [number myseq]
    (loop [i 0]
        (if (= i (count file))
            (println "done")
            (filter [x] (> x number))
                (:rank (first myseq))))
    (recur (rest myseq))))

最后是循环获得新的对序列的最有效方法吗?

4

2 回答 2

8

这里不需要循环/重复。filter 已经为您遍历了一个序列:

(filter (fn [entry] (> (:rank entry) 220)) myseq)
于 2012-07-24T08:14:33.460 回答
6

首先要知道的是,clojure 中的(大部分)数据结构是不可变的,并且大多数函数都是函数式的。这意味着,它们没有副作用。在您的情况下filter,不会以任何方式更改序列,它会返回一个新序列,仅包含未过滤的项目。

因此,要过滤,myseq您需要执行以下操作:

(def filtered-seq (filter (fn [x] ...) myseq))

过滤器会重复调用该函数,绑定x到当前过滤的项目myseq。也就是说,第一次将绑定到{:name "Peter" :rank 2222},然后绑定到{:name "Anna" :rank 111}。将filtered-seq仅包含函数返回 true 的元素。myseq不会修改!

因此,您只想留下:rank高于 222 的元素:

(filter (fn [x] (> (:rank x) 222)) myseq)

而已。关于过滤器的另一件事是,它很懒惰。也就是说,返回集合中的项目仅在需要时才“实现”(或计算)。

你不需要使用loop这个,因为filter工作很好,而且loop并不懒惰。

也就是说,你的loop不起作用,因为它有几个问题:

  1. recur在外面loop。在这种情况下,clojure 将循环回到函数的开头。
  2. 您需要构造一个返回值并且需要维护“当前”元素
  3. 您需要正确检查结束条件

代码可能看起来像这样(未经测试):

(defn remove-lower [number myseq]
  (loop [sq myseq res []]
     (if (empty? sq)
         res
         (let [current (first sq)]
           (if (> (:rank current) number)
              (recur (rest sq) (conj res current))
              (recur (rest sq) res))))))

注意如何:

  1. recur现在在里面loop
  2. res包含返回值并sq包含当前左序列
  3. eachrecur传递 和 的新值,sq用于res下一次迭代
  4. sq每次迭代都会“缩小”,因此循环最终将退出,除非myseq是无限的。将此与 进行对比filter,后者可以很好地处理无限序列。

正如你所看到的,这比而且更难读,更不通用filter,也很渴望(不是懒惰)。

于 2012-07-24T08:19:54.513 回答