2013-03-10 84 views
0

我想在clojure中写一个函数,它会返回一个映射给出一个形式为“key1 $ value1,key2 $ value2”的字符串。我想出了这个。如何防止这个clojure函数返回一个列表

(defn get-map 
    "Returns a map of key value pairs from a string formatted in the form 'key1$value1,key2$value2'" 
    [line] 
    (let [result {}] 
    (for [item (split line #",")] 
     (let [pair (split item #"\$")] 
     (assoc result (nth pair 0) 
      (if (= (.size pair) 2) (nth pair 1) "")))))) 

虽然这个代码的唯一问题是它返回列表中的地图。

=>(get-map "key1$value1,key2,value2") 
({"key1" "value1"} {"key2" "value2"}) 

我试图返回结果作为第一个表单的最后一个表达式,但结果是空的地图。

(defn get-map 
    "Returns a map of key value pairs from a string formatted in the form 'key1$value1,key2$value2'" 
    [line] 
    (let [result {}] 
    (for [item (split line #",")] 
     (let [pair (split item #"\$")] 
     (assoc result (nth pair 0) 
      (if (= (.size pair) 2) (nth pair 1) "")))) 
    result)) 

=>(get-map "key1$value1,key2,value2") 
{} 

两个问题 -

  1. 我应该如何修改只返回地图功能,无单包装?
  2. 为什么返回结果作为第一个表单的最后一个表达式返回空映射?

此外,如果你有建议,写一个更好,更习惯clojure方式,将不胜感激。

对问题1

回答

2

在Clojure中,for不是一个循环这是一个列表解析。基本上,它将 集合中的每个项目都绑定到您指定的任何名称(在这种情况下为item),然后评估 表达式(在此例中为let [pair...)。每个评估结果以 的顺序返回。

例如:

(for [item (split "key1$value1,key2$value2" #",")] 
    item) 
;returns: ("key1$value1" "key2$value2") 

在你的函数是for实际创建列表。由let绑定的名称是不可变的 因此,每次for评估其表达式result仍为空映射。这就是为什么你要得到一个地图列表 每个单一的条目。

这里的生产键值对的列表,使用的nth 的未发现的能力提供一个缺省的,如果$是缺少一个办法:

(for [item (split "key1$value1,key2$value2,key3" #",")] 
    (let [pair (split item #"\$")] 
    [(nth pair 0) (nth pair 1 "")])) 
;returns: (["key1" "value1"] ["key2" "value2"] ["key3" ""]) 

然后你可以使用into转那成一张地图。以下是完整的功能:

(defn get-map 
    "Returns a map of key value pairs from a string formatted in the form 'key1$value1,key2$value2'" 
    [line] 
    (into {} 
    (for [item (split line #",")] 
     (let [pair (split item #"\$")] 
     [(nth pair 0) (nth pair 1 "")])))) 
2
(defn get-map 
    "Returns a map of key value pairs from a string formatted in the form 'key1$value1,key2$value2'" 
    [line] 
    (->> (clojure.string/split line #",") 
     (mapcat #(clojure.string/split % #"\$")) 
     (apply hash-map))) 

user> (get-map "key1$value1,key2$value2") 
{"key1" "value1", "key2" "value2"} 

接听/提示: 对于总是返回序列。它是Clojure中的列表理解。

问题2的答案/提示: Clojure的数据结构是不可变的。这意味着关联现有的地图不会改变现有的地图,而是返回一个新的地图。

2

for表单确实懒惰的列表理解,因此不适合顺序更新结构。您可以使用for生成对的列表,并使用reduce将它们打包到地图中。或者,使用into,这确实降低:

(defn get-map [line] 
    (into {} 
    (for [item (split line #",")] 
     (split item #"\$")))) 

使用re-seq另一种可能的实现:

(defn get-map [s] 
    (into {} (map #(subvec % 1) (re-seq #"([^\$]+)\$([^,]+),*" s))))