Data structures Clojure:如何将懒散的映射条目转换为structmap?

Data structures Clojure:如何将懒散的映射条目转换为structmap?,data-structures,recursion,clojure,enlive,Data Structures,Recursion,Clojure,Enlive,我是clojure的新手,一直在使用enlive转换html文档的文本节点。我的最终目标是将结构转换回html、标记和所有内容 我目前能够获取enlive html/html资源返回的structmap,并使用 (apply str (html/emit* nodes)) 其中节点是structmap 我还可以根据自己的意愿转换structmap的:content文本节点。但是,在转换structmap的内容文本节点之后,我最终得到了一个lazyseq的MapEntries。我想将其转换回st

我是clojure的新手,一直在使用enlive转换html文档的文本节点。我的最终目标是将结构转换回html、标记和所有内容

我目前能够获取enlive html/html资源返回的structmap,并使用

(apply str (html/emit* nodes))
其中节点是structmap

我还可以根据自己的意愿转换structmap的:content文本节点。但是,在转换structmap的内容文本节点之后,我最终得到了一个lazyseq的MapEntries。我想将其转换回structmap,以便在其上使用emit*。这有点棘手,因为lazyseqs&structmap是嵌套的

tldr:

如何转换:

([:tag :html]
 [:attrs nil]
 [:content
  ("\n"
   ([:tag :head]
    [:attrs nil]
    [:content
     ("\n  "
      ([:tag :title] [:attrs nil] [:content ("Page Title")])
      "  \n")])
   "\n"
   ([:tag :body]
    [:attrs nil]
    [:content
     ("\n  "
      ([:tag :div]
       [:attrs {:id "wrap"}]
       [:content
        ("\n    "
         ([:tag :h1] [:attrs nil] [:content ("header")])
         "\n    "
         ([:tag :p] [:attrs nil] [:content ("some paragrah text")])
         "\n  ")])
      "\n")])
   "\n\n")])
进入:

更新 为我指出了中的
更新方向,我可以使用该方向修改地图,而无需将其转换为序列,从而使我的问题变得无关紧要

(defn modify-or-go-deeper
  "If item is a map, updates its content, else if it's a string, modifies it"
  [item]
  (declare update-content)
  (cond
    (map? item) (update-content item)
    (string? item) (modify-text item)))

(defn update-content
  "Calls modify-or-go-deeper on each element of the :content sequence"
  [coll]
  (update-in coll [:content] (partial map modify-or-go-deeper)))
我以前在地图上使用过
for
,但是
更新是一个不错的选择。

试试看

(def mp '([:tag :html] [:attrs nil] [:content
    (""
    ([:tag :head] [:attrs nil] [:content
        ("\n\t\t"
        ([:tag :title] [:attrs nil] [:content ("page title")])
        "\n\t\t")])
        "\n\t"
        ([:tag :body] [:attrs nil] [:content
            ("\n\t\t"
            ([:tag :div] [:attrs {:id "wrapper"}] [:content
            ("\n\t\t  "
            ([:tag :h1] [:attrs nil] [:content
                ("\n  \t\t\tpage title"
                ([:tag :br] [:attrs nil] [:content ()])
                "\n  \t\t\tand more title\n  \t\t")])
                "\n  \t\t"
                ([:tag :p] [:attrs nil] [:content
                    ("\n  \t\tSome paragraph text"
                    ([:tag :img] [:attrs {:src "images/image.png", :id "image"}] [:content nil])
                    "\n  \t\t")])
            "\n\t\t")]
            "\n\t     \n\t\t"))]
        "\n\n"))]))

(clojure.walk/postwalk (fn [x]
                         (if (and (list? x) (vector? (first x)))
                           (into {} x)
                           x))
                       mp)
它将抛出一个错误,但是如果您将输入更改为

([:tag :html]
 [:attrs nil]
 [:content
  (""
   ([:tag :head]
    [:attrs nil]
    [:content
     ("\n\t\t"
      ([:tag :title] [:attrs nil] [:content ("page title")])
      "\n\t\t")])
   "\n\t"
   ([:tag :body]
    [:attrs nil]
    [:content
     ("\n\t\t"
      ([:tag :div]
       [:attrs {:id "wrapper"}]
       [:content
        ("\n\t\t  "
         ([:tag :h1]
          [:attrs nil]
          [:content
           ("\n  \t\t\tpage title"
            ([:tag :br] [:attrs nil] [:content ()])
            "\n  \t\t\tand more title\n  \t\t")])
         "\n  \t\t"
         ([:tag :p]
          [:attrs nil]
          [:content
           ("\n  \t\tSome paragraph text"
            ([:tag :img]
             [:attrs {:src "images/image.png", :id "image"}]
             [:content nil])
            "\n  \t\t")])
         "\n\t\t")]
       ))]))]))
那就行了。不同之处在于,在已编辑的输入中,您正在从包含键值对的同一列表中删除类似“\n\t\t”的字符串。希望这有帮助

编辑: 以下几点对我很有用:

(def mp '([:tag :html]
 [:attrs nil]
 [:content
  (""
   ([:tag :head]
    [:attrs nil]
    [:content
     ("\n\t\t"
      ([:tag :title] [:attrs nil] [:content ("page title")])
      "\n\t\t")])
   "\n\t"
   ([:tag :body]
    [:attrs nil]
    [:content
     ("\n\t\t"
      ([:tag :div]
       [:attrs {:id "wrapper"}]
       [:content
        ("\n\t\t  "
         ([:tag :h1]
          [:attrs nil]
          [:content
           ("\n  \t\t\tpage title"
            ([:tag :br] [:attrs nil] [:content ()])
            "\n  \t\t\tand more title\n  \t\t")])
         "\n  \t\t"
         ([:tag :p]
          [:attrs nil]
          [:content
           ("\n  \t\tSome paragraph text"
            ([:tag :img]
             [:attrs {:src "images/image.png", :id "image"}]
             [:content nil])
            "\n  \t\t")])
         "\n\t\t")]
       ))]))]))

(clojure.walk/postwalk (fn [x]
                         (if (and (list? x) (vector? (first x)))
                           (into {} x)
                           x))
                       mp)
尝试复制并粘贴到repl中。您应该获得以下信息:

{:tag :html,
 :attrs nil,
 :content
 (""
  {:tag :head,
   :attrs nil,
   :content
   ("\n\t\t"
    {:tag :title, :attrs nil, :content ("page title")}
    "\n\t\t")}
  "\n\t"
  {:tag :body,
   :attrs nil,
   :content
   ("\n\t\t"
    {:tag :div,
     :attrs {:id "wrapper"},
     :content
     ("\n\t\t  "
      {:tag :h1,
       :attrs nil,
       :content
       ("\n  \t\t\tpage title"
        {:tag :br, :attrs nil, :content ()}
        "\n  \t\t\tand more title\n  \t\t")}
      "\n  \t\t"
      {:tag :p,
       :attrs nil,
       :content
       ("\n  \t\tSome paragraph text"
        {:tag :img,
         :attrs {:src "images/image.png", :id "image"},
         :content nil}
        "\n  \t\t")}
      "\n\t\t")})})}

只需将所有内容放回地图,然后递归地遍历内容

(defn into-xml
  [coll]
  (let [tag (into {} coll)]
    (update-in tag [:content] (partial map into-xml))))
请注意,仅当您访问内容时才会对其进行转换

编辑:Woops,错过了字符串部分。这里有一个工作版本:

(defn into-xml
  [coll]
  (if-not (string? coll)
    (let [tag (into {} coll)]
      (update-in tag [:content] (partial map into-xml)))
    coll))

java.lang.RuntimeException:java.lang.RuntimeException:java.lang.RuntimeException:java.lang.RuntimeException:java.lang.ClassCastException:java.lang.Character无法强制转换为java.util.Map$EntryHey,我实际上没有收到错误,因此可能是我粘贴的输入不正确。但这似乎根本没有改变集合,它仍然返回相同的lazyseq MapEntries。就像我说的,我是clojure的新手,所以我不确定我是否正确使用了它,但我把它放入了一个函数中,比如:
(defn-retransform[mp](clojure.walk/postwark(fn[x](if(and(list?x)(vector?(first x))(into{}x)mp))
嗯,这对我来说很有效。我已经更新了我的帖子,上面有一个例子,你可以直接复制粘贴。谢谢。这似乎很管用,但一旦进入内容序列就会出错。将我的集合传递到此函数中,我得到:
{:tag:html,:attrs nil,:content(IllegalArgumentException不知道如何从以下位置创建ISeq:java.lang.Character clojure.lang.RT.seqFrom(RT.java:487)
我现在正在使用你的解决方案,看看我是否能弄清楚发生了什么。嘿,正如我所说,我是clojure的新手。你的解决方案为我指明了在
中更新的
方向,我可以在原始集合中使用它,而不是在
中使用
,因此我保留了地图结构,而不是转换为MapEntries的序列。我在问题的最后给出了我如何遍历集合的解决方案。谢谢!@jmw啊,是的。字符串。我添加了一个固定版本,但您的解决方案当然更好,首先使用
更新在
中。不过请注意,
声明
应该在顶层。对在实践中,我会敦促读者使用
trampoline
,避免相互递归的陷阱。。。
(defn into-xml
  [coll]
  (if-not (string? coll)
    (let [tag (into {} coll)]
      (update-in tag [:content] (partial map into-xml)))
    coll))