Clojure 2d列表到哈希映射

时间:2013-04-09 00:37:28

标签: clojure hashmap lazy-sequences

我有一个无限的列表:
((1 1)(3 9)(5 17)......)
我想用它制作哈希图:
{:1 1:3 9:5 17 ...)

基本上,“内部”列表的第一个元素是关键字,第二个元素是值。我不确定在创建时创建我使用的列表是否更容易:

  

(iterate(fn [[a b]] [(a的计算)(b的计算)])[1 1])

(b)的计算需要(a)所以我相信在这一点上(a)不能成为一个关键词......这一点的全部意义在于,人们可以轻易地获得给定(a)的值(b)。

非常感谢任何想法...

- EDIT--
好的,我明白了:

  

(def my-map(into {}(map#(hash-map(keyword(str(first%)))(first(rest%)))my-list)))

问题是:它似乎并不是懒惰的......即使我没有消耗它,它也会永远存在。有没有办法强迫它变懒?

5 个答案:

答案 0 :(得分:3)

问题是哈希映射既不是无限的也不是懒惰的。它们专为快速键值访问而设计。因此,如果您有哈希映射,您将能够执行快速键查找。键值访问是哈希映射的核心思想,但它使得创建惰性无限哈希映射变得不可能。

假设我们有一个无限的2d列表,那么你可以使用into创建哈希映射:

(into {} (vec (map vec my-list)))

无法使这个哈希图无限。因此,唯一的解决方案是创建自己的哈希映射,如Chouser suggested。在这种情况下,你将拥有一个无限的2d序列和一个在其中执行惰性密钥查找的函数。

实际上,他的解决方案可以略微改进:

(def my-map (atom {}))

(def my-seq (atom (partition 2 (range))))

(defn build-map [stop]
  (when-let [[k v] (first @my-seq)]
    (swap! my-seq rest)
    (swap! my-map #(assoc % k v))
    (if (= k stop)
        v
        (recur stop))))

(defn get-val [k]
  (if-let [v (@my-map k)]
    v
    (build-map k)))
我的示例中的

my-map存储当前的哈希映射,my-seq存储尚未处理的元素的序列。 get-val函数使用my-map中已处理的元素执行惰性查找,以提高其性能:

(get-val 4)
=> 5
@my-map
=> {4 5, 2 3, 0 1}

加速:

(time (get-val 1000))
=> Elapsed time: 7.592444 msecs
(time (get-val 1000))
=> Elapsed time: 0.048192 msecs

答案 1 :(得分:1)

如果你将它压平到一个(k v k v k v k v)列表并使其变平,那么你可以使用apply来调用该列表的hash-map作为它的参数,它将引导你寻找的列表。

user> (apply hash-map (flatten '((1 1)(3 9)(5 17))))
{1 1, 3 9, 5 17}

虽然没有关键字化第一个参数。

至少在clojure 中,与键关联的最后一个值被称为该键的值。如果不是这种情况,则无法为映射中已存在的键生成具有不同值的新映射,因为查找函数将返回第一个(现在为阴影的键)。如果查找函数搜索到结尾,那么它不是懒惰的。您可以通过编写自己的使用关联列表的地图实现来解决这个问题,尽管它缺乏Clojure基于trei的地图的性能保证,因为它会在最坏的情况下转换为线性时间。

我不确定保持输入序列是否具有所需的结果。

答案 2 :(得分:1)

为了保持懒惰,计算机必须在每次请求密钥时对输入序列进行线性扫描,至少如果密钥超出了目前已扫描的密钥。一个天真的解决方案就是每次扫描序列,如下所示:

(defn get-val [coll k]
  (some (fn [[a b]] (when (= k a) b)) coll))

(get-val '((1 1)(3 9)(5 17))
         3)
;=> 9

稍微不那么天真的解决方案是使用memoize来缓存get-val的结果,尽管这仍然会扫描输入序列而不是严格必要的。一个更积极的缓存解决方案是使用一个原子(如内部memoize所做的那样)来缓存看到的每一对,从而在查找需要尚未看到的东西时只消耗更多的输入序列。

无论如何,我不建议将其包装在哈希映射API中,因为这意味着有效的不可变“更新”可能不需要而且很难实现。我通常也不建议将密钥用于关键字。

答案 3 :(得分:0)

要从序列中创建一个hashmap,您可以尝试:

(defn to-map [s] (zipmap (map (comp keyword str first) s) (map second s)))

=> (to-map '((1 1)(3 9)(5 17)))
=> {:5 17, :3 9, :1 1}

答案 4 :(得分:0)

您可以稍后将该结构转换为哈希图

(def it #(iterate (fn [[a b]] [(+ a 1) (+ b 1)]) [1 1])) 
(apply hash-map (apply concat (take 3 (it))))
=> {1 1, 2 2, 3 3}