1

我想在 clojure 中编写一个函数,它会返回一个地图,给出一个形式为“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) ""))))))

虽然它有效,但此代码的唯一问题是它在列表中返回地图。

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

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

(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. 为什么将结果作为第一个 let 表单的最后一个表达式返回一个空映射?

此外,如果您有建议以更好、更惯用的 clojure 方式编写相同的函数,我们将不胜感激。

4

3 回答 3

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"}

问题 1 的答案/提示: For 总是返回一个序列。它是 Clojure 中的列表推导。

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

于 2013-03-10T09:45:29.980 回答
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))))
于 2013-03-10T15:29:34.567 回答
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 "")]))))
于 2013-03-10T16:19:55.987 回答