2

我正在试验 Clojure 和 Instaparse。我创建了一种小型玩具语言,但我陷入了如何正确处理生成的树的困境。这就是我得到的:

[:ClassDescription 
 [:ClassName "Test"]
 [:Properties 
  [:Property 
   [:PropertyName "ID"] 
   [:PropertyType "Int"]] 
  [:Property 
   [:PropertyName "Name"] 
   [:PropertyType "string"]]]]

现在,作为一个例子,我想提取所有的 PropertyTypes。我有两种主要方式,我想为这两种方式提供解决方案。

  1. 通过指定路径;就像是[:ClassDescription :Properties :Property :PropertyType]
  2. 通过提取所有 :PropertyType元素,无论深度如何。

对于 A.,我的第一个想法是通过将它的某些部分转换为映射,insta/transform然后使用.get-inget-in

我也可以使用nth, 并将自己钻入结构中,但这看起来很麻烦,并且如果我添加另一层很容易损坏。

我的另一个想法是递归解决方案,我以相同的方式处理每个元素并循环遍历它并检查所有匹配项。

对于 B. 到目前为止,我唯一的解决方案是一个递归函数,它只是钻取所有内容并尝试匹配第一个元素。

我相信这些“手写”功能可以通过 , , , 等的巧妙组合来避免insta/transformmap可以filterreduce

4

1 回答 1

4

为了从解析的数据中获取元素,您可以使用tree-seq迭代所有并选择您需要的内容。例如:

(defn parsed-tree-seq [parsed]
  (tree-seq #(vector? (second %)) rest parsed))

(map second
     (filter
       #(= (first %) :PropertyType)
       (parsed-tree-seq parsed)))
; => ("Int" "string")

然而,您可能最好先通过<...>在解析器中使用和/或将它们变成更贴图的东西来塑造您的数据,通过转换更容易访问。例如将:Properties变成地图列表并将整个事物变成地图:

(defn transform [parsed]
  (insta/transform
    {:Property #(into {} %&)
     :Properties #(vec (concat [:Properties] [%&]))
     :ClassDescription #(into {} %&)
     } parsed))

(map :PropertyType (:Properties (transform parsed)))
; => ("Int" "string")
于 2015-04-10T10:31:47.573 回答