对clojure中一个懒惰的哈希映射序列进行排序

时间:2015-09-04 09:20:01

标签: clojure hashmap quicksort lazy-sequences

我需要从数百万个哈希映射的懒惰序列中获取20个结果,但是20个基于对哈希映射中的各种值进行排序。

例如:

(def population [{:id 85187153851 :name "anna" :created #inst "2012-10-23T20:36:25.626-00:00" :rank 77336}
         {:id 12595145186 :name "bob" :created #inst "2011-02-03T20:36:25.626-00:00" :rank 983666}
         {:id 98751563911 :name "cartmen" :created #inst "2007-01-13T20:36:25.626-00:00" :rank 112311}
         ...
         {:id 91514417715 :name "zaphod" :created #inst "2015-02-03T20:36:25.626-00:00" :rank 9866}]

在正常情况下,简单的sort-by可以完成工作:

(sort-by :id population)
(sort-by :name population)
(sort-by :created population)
(sort-by :rank population)

但是我需要尽可能快地在数百万条记录中执行此操作,并且想要懒惰而不是必须实现整个数据集。

我环顾四周,发现了许多算法的实现,这些算法非常适合于排序一系列值(主要是数字),但是对于我需要的方式来说,哈希映射的延迟序列都没有。

速度&效率是最重要的,我发现的最好的一直是Joy Of Clojure书(第6.4章)中的快速举例,该书确实足以返回所需的结果。

(ns joy.q)

(defn sort-parts
  "Lazy, tail-recursive, incremental quicksort.  Works against
   and creates partitions based on the pivot, defined as 'work'."
  [work]
  (lazy-seq
   (loop [[part & parts] work]
     (if-let [[pivot & xs] (seq part)]
       (let [smaller? #(< % pivot)]
         (recur (list*
                 (filter smaller? xs)
                 pivot
                 (remove smaller? xs)
                 parts)))
       (when-let [[x & parts] parts]
         (cons x (sort-parts parts)))))))

(defn qsort [xs]
    (sort-parts (list xs))) 

工作得很好......

(time (take 10 (qsort (shuffle (range 10000000)))))
"Elapsed time: 551.714003 msecs"
(0 1 2 3 4 5 6 7 8 9)

大!但...

无论我尝试多少,我似乎无法解决如何将其应用于一系列哈希图。

我需要类似的东西:

(take 20 (qsort-by :created population))

1 个答案:

答案 0 :(得分:4)

如果你只需要前N个元素,那么完全排序太昂贵了(甚至像JoC中那样懒惰排序:它需要保留几乎所有数据集在内存中)。

您只需扫描(=> (defn top-by [n k coll] (reduce (fn [top x] (let [top (conj top x)] (if (> (count top) n) (disj top (first top)) top))) (sorted-set-by #(< (k %1) (k %2))) coll)) #'user/top-by => (top-by 3 first [[1 2] [10 2] [9 3] [4 2] [5 6]]) #{[5 6] [9 3] [10 2]} )数据集并保留目前为止最好的N项。

SELECT a.*,   
lag(amount,1) over (PARTITION BY bond_number ORDER BY 
payment_id,jn_date_time)recent_amount,   
amount + nvl(lag(amount,1) over (PARTITION BY bond_number ORDER BY  
payment_id,jn_date_time),0) running_total  
FROM JOURNAL a  
ORDER BY payment_id,jn_date_time