Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/clojure/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Clojure 矢量地图化_Clojure - Fatal编程技术网

Clojure 矢量地图化

Clojure 矢量地图化,clojure,Clojure,我有这个字段列表(这是Facebook的graph API字段列表) 我想从中生成一张地图。约定如下,如果在键向量之后,则其为键的内部对象。示例向量可以表示为地图,如下所示: {"a" "value" "b" {"c" {"t" "value"} "d" "value"} "e" {"f" "value"} "g" "value"} 到目前为止,我有这个解决方案 (defn traverse [data] (mapcat (fn [[left right]]

我有这个字段列表(这是Facebook的graph API字段列表)

我想从中生成一张地图。约定如下,如果在键向量之后,则其为键的内部对象。示例向量可以表示为地图,如下所示:

{"a" "value"
 "b" {"c" {"t" "value"} "d" "value"}
 "e" {"f" "value"}
 "g" "value"}
到目前为止,我有这个解决方案

(defn traverse
  [data]
  (mapcat (fn [[left right]]
            (if (vector? right)
              (let [traversed (traverse right)]
                (mapv (partial into [left]) traversed))
              [[right]]))
          (partition 2 1 (into [nil] data))))

(defn facebook-fields->map
  [fields default-value]
  (->> fields
       (traverse)
       (reduce #(assoc-in %1 %2 nil) {})
       (clojure.walk/postwalk #(or % default-value))))

(let [data ["a" "b" ["c" ["t"] "d"] "e" ["f"] "g"]]
  (facebook-fields->map data "value"))
#=> {"a" "value", "b" {"c" {"t" "value"}, "d" "value"}, "e" {"f" "value"}, "g" "value"}

但这是脂肪和难以遵循。我想知道是否有更优雅的解决方案。

这里有另一种方法,在整个遍历过程中使用
postwark
,而不是仅在
默认值
替换时使用:

(defn facebook-fields->map
  [fields default-value]
  (clojure.walk/postwalk
    (fn [v] (if (coll? v)
              (->> (partition-all 2 1 v)
                   (remove (comp coll? first))
                   (map (fn [[l r]] [l (if (coll? r) r default-value)]))
                   (into {}))
              v))
    fields))

(facebook-fields->map ["a" "b" ["c" ["t"] "d"] "e" ["f"] "g"] "value")
=> {"a" "value",
    "b" {"c" {"t" "value"}, "d" "value"},
    "e" {"f" "value"},
    "g" "value"}

这里有另一种方法,可以在整个遍历过程中使用
postwark
,而不是仅在
default value
替换时使用:

(defn facebook-fields->map
  [fields default-value]
  (clojure.walk/postwalk
    (fn [v] (if (coll? v)
              (->> (partition-all 2 1 v)
                   (remove (comp coll? first))
                   (map (fn [[l r]] [l (if (coll? r) r default-value)]))
                   (into {}))
              v))
    fields))

(facebook-fields->map ["a" "b" ["c" ["t"] "d"] "e" ["f"] "g"] "value")
=> {"a" "value",
    "b" {"c" {"t" "value"}, "d" "value"},
    "e" {"f" "value"},
    "g" "value"}

试图阅读大量嵌套的代码会让我头疼。更糟糕的是,当答案是“强制配合”的时候,
postwark
,它以一种“由内而外”的方式做事。另外,使用
分区all
有点浪费,因为我们需要丢弃任何具有两个非向量的对

对我来说,最自然的解决方案是简单的自顶向下递归。唯一的问题是,我们事先不知道是否需要从输入序列的头部删除一个或两个项目。因此,我们不能对循环或映射使用简单的

因此,只需将其编写为一个简单的递归,并使用
if
来确定我们是从列表的开头消费1项还是2项

  • 如果第二个项目是一个值,我们消费一个项目并添加
    :创建映射条目的虚拟值
    
  • 如果第二项是向量,我们递归并使用它 作为映射条目中的值
  • 守则:

    (ns tst.demo.core
      (:require [clojure.walk :as walk] ))
    
    (def data ["a" "b" ["c" ["t"] "d"] "e" ["f"] "g"])
    
    (defn parse [data]
      (loop [result {}
             data   data]
        (if (empty? data)
          (walk/keywordize-keys result)
          (let [a (first data)
                b (second data)]
            (if (sequential? b)
              (recur
                (into result {a (parse b)})
                (drop 2 data))
              (recur
                (into result {a :dummy-value})
                (drop 1 data)))))))
    
    结果:

    (parse data) => 
    {:a :dummy-value,
     :b {:c {:t :dummy-value}, :d :dummy-value},
     :e {:f :dummy-value},
     :g :dummy-value}
    

    我在最后添加了
    keywordize键
    ,只是为了让结果更加“Clojurey”。

    试图读取嵌套的代码会让我头疼。更糟糕的是,当答案是“强制配合”的时候,
    postwark
    ,它以一种“由内而外”的方式做事。另外,使用
    分区all
    有点浪费,因为我们需要丢弃任何具有两个非向量的对

    对我来说,最自然的解决方案是简单的自顶向下递归。唯一的问题是,我们事先不知道是否需要从输入序列的头部删除一个或两个项目。因此,我们不能对
    循环或映射使用简单的

    因此,只需将其编写为一个简单的递归,并使用
    if
    来确定我们是从列表的开头消费1项还是2项

  • 如果第二个项目是一个值,我们消费一个项目并添加
    :创建映射条目的虚拟值
    
  • 如果第二项是向量,我们递归并使用它 作为映射条目中的值
  • 守则:

    (ns tst.demo.core
      (:require [clojure.walk :as walk] ))
    
    (def data ["a" "b" ["c" ["t"] "d"] "e" ["f"] "g"])
    
    (defn parse [data]
      (loop [result {}
             data   data]
        (if (empty? data)
          (walk/keywordize-keys result)
          (let [a (first data)
                b (second data)]
            (if (sequential? b)
              (recur
                (into result {a (parse b)})
                (drop 2 data))
              (recur
                (into result {a :dummy-value})
                (drop 1 data)))))))
    
    结果:

    (parse data) => 
    {:a :dummy-value,
     :b {:c {:t :dummy-value}, :d :dummy-value},
     :e {:f :dummy-value},
     :g :dummy-value}
    

    我在末尾添加了
    关键字
    ,只是为了让结果更“Clojurey”。

    因为你要求的是一个更干净的解决方案,而不是一个解决方案,因为我认为这是一个整洁的小问题,这里还有一个

    (defn facebook-fields->map [coll]
      (into {}
            (keep (fn [[x y]]
                    (when-not (vector? x)
                      (if (vector? y)
                        [x (facebook-fields->map y)]
                        [x "value"]))))
            (partition-all 2 1 coll)))
    

    既然你要求的是一个更干净的解决方案,而不是一个解决方案,而且因为我认为这是一个整洁的小问题,这里还有一个

    (defn facebook-fields->map [coll]
      (into {}
            (keep (fn [[x y]]
                    (when-not (vector? x)
                      (if (vector? y)
                        [x (facebook-fields->map y)]
                        [x "value"]))))
            (partition-all 2 1 coll)))
    

    这里有一个专门的网站,因为这里有一个专门的网站,我觉得@alan thompson的代码更容易阅读。也许只是因为它反映了我对这个问题的看法。没有
    comp
    ->
    ,解构,没有一行带有anon-fn和
    if
    ,所有这些都会给读者带来一些压力。就我个人而言,我觉得@alan thompson的代码更容易阅读。也许只是因为它反映了我对这个问题的看法。没有
    comp
    ->
    ,解构,没有一行带有anon-fn和
    if
    ,所有这些都会给读者带来一些压力。只是我谦逊的2ct。很好!非常简洁易懂!非常简洁易懂