1

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

最初の 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")
{}

2つの質問 -

  1. リストラッパーなしでマップだけを返すように関数を変更するにはどうすればよいですか?
  2. 最初の let フォームの最後の式として結果を返すと、空のマップが返されるのはなぜですか?

また、同じ関数をより良い、より慣用的な clojure の方法で書くための提案があれば、それを歓迎します。

4

3 に答える 3

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たびに空のマップのままになります。そのため、それぞれに 1 つのエントリを持つマップのリストを取得しています。forresult

nth $ が欠落している場合にデフォルトを提供するnot-found 機能を使用するキーと値のペアのリストを生成する方法を次に示します。

(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 に答える
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 に答える