specter, 缺少 Clojure(Script) 块

分享于 

14分钟阅读

GitHub

  繁體 雙語
Clojure(Script)'s missing piece
  • 源代码名称:specter
  • 源代码网址:http://www.github.com/nathanmarz/specter
  • specter源代码文档
  • specter源代码下载
  • Git URL:
    git://www.github.com/nathanmarz/specter.git
    Git Clone代码到本地:
    git clone http://www.github.com/nathanmarz/specter
    Subversion代码到本地:
    $ svn co --depth empty http://www.github.com/nathanmarz/specter
    Checked out revision 1.
    $ cd repo
    $ svn up trunk
    
    Build Status

    瀑布拒绝了对不可以变数据结构操作的clojure方法的限制,而是公开一个优雅的API来允许。 with特别优秀于查询和转换嵌套和递归数据,重要的用例是非常复杂的。

    ghost有一个非常简单的内核,仅仅是一个称为"导航器"的抽象。 查询和转换是通过将导航器精确地定位到你想要检索或者更改的"路径"来完成的。 导航器可以与任何其他导航器组合使用,从而可以非常简洁地表达复杂的操作。

    此外,ghost具有性能胜过手工优化的代码( 请参见这个基准测试。)。 只在 get-inupdate-in 中内置的clojure是和,并且Specter和 85%分别是1 和。 在幕后,ghost使用了动态技术来去除组合的开销。

    数据处理的Clojure方法和ghost方法之间存在一些关键的区别。 ,总是使用最有效的方法来实现对数据类型的操作( 比如 )。 last vs last )。Clojure有意留出许多操作,例如前进到向量或者插入到序列中间。 ghost拥有涵盖这些用例( BEFORE-ELEMbefore-index ) 和更多的导航器。 最后,瀑布转换总是一个数据结构的精确部分,它的中的所有东西都是相同的。 例如 ALL 针对序列中的每个值,而结果转换总是与输入( 比如 ) 相同。 矢量保持向量,排序的贴图保留排序的贴图)。

    请考虑以下示例:

    例 1: 增加图向量映射中每个偶数的递增

    (defdata {:a [{:aa1:bb2}
     {:cc3}]
     :b [{:dd4}]});; Manual Clojure(defnmap-vals [m afn]
     (->> m (map (fn [[k v]] [k (afn v)])) (into (empty m))))
    (map-vals data
     (fn [v]
     (mapv (fn [m]
     (map-vals m
     (fn [v] (if (even? v) (inc v) v))))
     v)));; Specter(transform [MAP-VALS ALL MAP-VALS even?] inc data)

    收费示例:将一系列元素附加到嵌套矢量

    (defdata {:a [123]});; Manual Clojure(update data :a (fn [v] (into (if v v []) [45])));; Specter(setval [:a END] [45] data)

    示例 3: 递增序列中最后一个奇数

    (defdata [12345678]);; Manual Clojure(let [idx (reduce-kv (fn [res i v] (if (odd? v) i res)) nil data)]
     (if idx (update data idx inc) data));; Specter(transform [(filterer odd?) LAST] inc data)

    收费示例 4: 在不改变序列类型的类型或者顺序的情况下,将函数映射到序列

    ;; Manual Clojure(map inc data) ;; doesn't work, becomes a lazy sequence(into (empty data) (map inc data)) ;; doesn't work, reverses the order of lists;; Specter(transform ALL inc data) ;; works for all Clojure datatypes with near-optimal efficiency
    最新版本

    Specter的最新版本位于 Clojars 上:

    Current Version

    学习幽灵
    • 简介博文:未完成 clojure Fragment。
    • 关于幽灵:幽灵: 强大而简单的数据结构操作。
      • 注意这个演示是在specter编译/缓存系统开发之前给出的。 你不再需要做任何特殊的事情来接近最佳性能。
    • Screencast: 了解鬼。
    • 使用示例的导航器列表: API文档相比,这个wiki页面提供了比API文档更全面的概述,并包含了许多示例。
    • 核心操作和定义新的导航器: 核心 select/transform/etc. 操作的API文档和定义新导航器的操作相比,这个wiki页面提供了更加全面的概述。
    • 这个wiki页面解释如何使用Specter进行精确和高效的递归导航。
    • 这个wiki页面提供了一个全面的概述,介绍如何使用specter导航器。 Zippers是一种较慢的导航方法,但是可以执行某些任务,这些任务对于specter导航器来说是不可能的。 请注意,拉链很少需要。
    • 备忘单
    • API文档
    • 性能指南:这个帖子提供了如何实现它的性能的概述。

    ghost的API包含在这些文件中:

    问题?

    你可以通过打开一个问题,在Github上提问。

    你还可以在 Clojurians的#specter 通道中找到帮助。

    示例

    增加地图地图中的所有值:

    user> (use 'com.rpl.specter)
    user> (transform [MAP-VALS MAP-VALS]
     inc
     {:a {:aa1} :b {:ba-1:bb2}})
    {:a {:aa2}, :b {:ba0, :bb3}}

    递增所有偶数值:映射序列中的键:

    user> (transform [ALL :a even?]
     inc
     [{:a1} {:a2} {:a4} {:a3}])
    [{:a1} {:a3} {:a5} {:a3}]

    从序列序列中检索每一个可以被 3整除的数字:

    user> (select [ALL ALL #(=0 (mod % 3))]
     [[1234] [] [53218] [246] [12]])
    [3318612]

    递增序列中最后一个奇数:

    user> (transform [(filterer odd?) LAST]
     inc
     [2136948])
    [21361048]

    从嵌套序列中删除 nils:

    user> (setval [:a ALL nil?] NONE {:a [12nil3nil]})
    {:a [123]}

    从嵌套映射中删除键/值对:

    user> (setval [:a:b:c] NONE {:a {:b {:c1}}})
    {:a {:b {}}}

    从嵌套映射中删除键/值对,删除在路径中变成空的贴图:

    user> (setval [:a (compact:b:c)] NONE {:a {:b {:c1}}})
    {}

    递增索引 1 ( 包含) 和 4 ( 独占) 之间的所有奇数:

    user> (transform [(srange14) ALL odd?] inc [01234567])
    [02244567]

    用 [:a :b :c :d :e] 将索引 2的子序列替换为 4:

    user> (setval (srange24) [:a:b:c:d:e] [0123456789])
    [01:a:b:c:d:e456789]

    将序列 [:a :b] 连接到序列的每个嵌套序列:

    user> (setval [ALL END] [:a:b] [[1] '(12) [:c]])
    [[1:a:b] (12:a:b) [:c:a:b]]

    从数据结构中获取所有数字,无论它们是如何嵌套的:

    user> (select (walker number?)
     {2 [12 [67]] :a4:c {:a1:d [2nil]}})
    [21212674]

    使用字符串键导航:

    user> (select ["a""b"]
     {"a" {"b"10}})
    [10]

    反转索引 4和 11之间所有偶数的位置:

    user> (transform [(srange411) (filterer even?)]
     reverse
     [0123456789101112131415])
    [0123105876941112131415]

    在每个至少有两个数字的子序列中追加 [:c :d]:

    user> (setval [ALL
     (selected? (filterer even?) (view count) (pred>=2))
     END]
     [:c:d]
     [[123456] [70-1] [88] []])
    [[123456:c:d] [70-1] [88:c:d] []]

    当进行更多涉及转换时,在数据结构内部导航时常常会发现丢失上下文,并且需要信息结构来执行转换,从而实现转换。 Specter通过允许在转换过程中使用值在转换函数中使用来解决这个问题。 以下是一个示例,通过将:b 键的值添加到:a 键的值,可以将映射序列转换为:

    user> (transform [ALL (collect-one:b) :a even?]
     +
     [{:a1:b3} {:a2:b-10} {:a4:b10} {:a3}])
    [{:b3, :a1} {:b-10, :a-8} {:b10, :a14} {:a3}]

    转换函数接收所有收集到的值和导航到的值作为参数。 在这种情况下,+ 接收:b 键的值后跟:a 键的值,并且转换执行的值为:a。

    收集值的四种方法是 VALcollectcollect-oneputvalVAL 只添加当前位于值列表中的任何元素,而 collectcollect-one 则在选择器中导航到所需的值。 collect 就像 select 一样工作,通过查找一系列值,collect-one 期望只导航到一个值。 最后,putval 在收集的值列表中添加一个外部值。

    增加值:按 10键:

    user> (transform [:a (putval10)]
     +
     {:a1:b3})
    {:b3:a11}

    如果:a 是偶数,则为序列中的每个映射递增值:如果为偶数,则为:偶数或者增量:

    user> (transform [ALL (if-path [:a even?] [:c ALL] :d)]
     inc
     [{:a2:c [12] :d4} {:a4:c [010-1]} {:a-1:c [111] :d1}])
    [{:c [23], :d4, :a2} {:c [1110], :a4} {:c [111], :d2, :a-1}]

    "协议路径on可以用于在多态数据中导航。 例如如果你有两种存储"账号"信息的方法:

    (defrecordAccount [funds])
    (defrecordUser [account])
    (defrecordFamily [accounts-list])

    可以根据当前导航到的元素的类型,使"accountpath"动态选择它的路径:

    (defprotocolpathAccountPath [])
    (extend-protocolpath AccountPath
     User :account Family [:accounts-list ALL])

    下面是如何从 UserFamily 列表中选择所有资金的方法:

    user> (select [ALL AccountPath :funds]
     [(->User (->Account50))
     (->User (->Account51))
     (->Family [(->Account1)
     (->Account2)])
     ])
    [505112]

    下面的示例演示递归导航。 在树中双击所有偶数的方法如下:

    (defprotocolpathTreeWalker [])
    (extend-protocolpath TreeWalker
     Object nil clojure.lang.PersistentVector [ALL TreeWalker])
    (transform [TreeWalker number? even?] #(*2 %) [:a1 [2 [[[3]]] :e] [45 [67]]]);; => [:a 1 [4 [[[3]]] :e] [8 5 [12 7]]]

    下面是如何在树( 基于深度优先搜索的顺序) 中反转所有偶数的位置。 本示例使用条件导航代替协议路径来进行漫游:

    (defTreeValues (recursive-path [] p
     (if-path vector?
     [ALL p]
     STAY
     )))
    (transform (subselect TreeValues even?)
     reverse
     [12 [3 [[4]] 5] [6 [78] 9 [[10]]]]
     );; => [1 10 [3 [[8]] 5] [6 [7 4] 9 [[2]]]]
    ClojureScript

    Specter支持 ClojureScript 然而,Clojure和ClojureScript之间的一些差异会影响你在ClojureScript中使用ghost的方式,特别是使用名称空间声明。 在Clojure中,你可以使用 (use'com.rpl.specter)(:require [com.rpl.specter :refer :all]) 但在ClojureScript中,这些选项不允许使用。 相反,请考虑使用以下选项之一:

    (:require [com.rpl.specter :as s])
    (:require [com.rpl.specter :as s :refer-macros [select transform]]) ;; add in the Specter macros that you need
    的未来工作
    • 将ghost与其他类型的数据结构( 如图) 集成。 愿望导航包括:减少拓扑顺序,导航到 outgo/传入节点,到子图(。带有指示如何在变换中附着外部边的元数据),到 node 属性,到 node 值,到特定节点。
    许可证

    版权所有 2015 -2018红星行星实验室。 Specter在Apache许可v2.0下许可。


    Clojure  
    相关文章