是否可以使用拉链操纵嵌套地图的矢量?

时间:2016-02-13 01:58:17

标签: clojure

我需要通过应用以下两个规则将以下输入转换为输出:

  1. 删除所有以“nope”作为最后一项
  2. 的向量
  3. 删除每个没有至少一个向量的地图,其中“ds1”作为最后一项
  4. (def input
      [{:simple1 [:from [:simple1 'ds1]]}
       {:simple2 [:from-any [[:simple2 'nope] [:simple2 'ds1]]]}
       {:walk1 [:from [:sub1 :sub2 'ds1]]}
       {:unaffected [:from [:unaffected 'nope]]}
       {:replaced-with-nil [:from [:the-original 'ds1]]}
       {:concat1 [:concat [[:simple1 'ds1] [:simple2 'ds1]]]}
       {:lookup-word [:lookup [:word 'word :word 'ds1]]}])
    
    (def output
      [{:simple1 [:from [:simple1 'ds1]]}
       {:simple2 [:from-any [[:simple2 'ds1]]]}
       {:walk1 [:from [:sub1 :sub2 'ds1]]}
       {:replaced-with-nil [:from [:the-original 'ds1]]}
       {:concat1 [:concat [[:simple1 'ds1] [:simple2 'ds1]]]}
       {:lookup-word [:lookup [:word 'word :word 'ds1]]}])
    

    我想知道拉链是否可以进行这种转换?

2 个答案:

答案 0 :(得分:0)

我建议使用clojure.walk代替这种常规树转换。修改替换函数可能需要花费一些时间,但它可以很好地处理Clojure数据结构的任何嵌套,在基于拉链的方法中,AFAIK可能会更具挑战性。

我们希望收缩我们的树,所以postwalk是我的首选。它需要一个函数f和一个树根,然后遍历树,用(f leaf)替换每个叶子值,然后用父母和他们的父母等替换根,直到最后替换根。 (prewalk类似,但是从根向下延伸到树叶,所以当你通过分割树枝来种植树时通常会更自然。)
这里的策略是以某种方式构造一个函数,修剪符合我们的删除标准的任何分支,但返回任何其他值不变。

(ns shrink-tree
  (:require [clojure.walk :refer [postwalk]]))

(letfn[(rule-1 [node] 
         (and (vector? node) 
              (= 'nope (last node))))
       (rule-2 [node] 
         (and 
           (map? node) 
           (not-any? #(and (vector? %) (= 'ds1 (last %))) 
                     (tree-seq vector? seq (-> node vals first)))))
       (remove-marked [node] 
                      (if (coll? node) 
                        (into (empty node) (remove (some-fn rule-1 rule-2) node)) 
                        node))]
  (= output (postwalk remove-marked input)))
;; => true

此处fns rule-1rule-2会尝试将您的规则转换为谓词和remove-marked

  1. 如果某个节点是一个集合,则返回相同的集合,减去使用该成员调用时rule1rule2返回真实的所有成员。要同时检查任何一个,我们将谓词与some-fn组合在一起。
  2. 否则返回相同的节点。这就是我们保持'ds1:from-any等值的方式。

答案 1 :(得分:0)

您可能还想考虑查看specter。它允许您选择和转换任意复杂的结构,从而支持这些类型的转换。