2013-03-10 85 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))))