项目作者: inaka

项目描述 :
Generic Zipper implementation in Erlang
高级语言: Erlang
项目地址: git://github.com/inaka/zipper.git
创建时间: 2014-09-05T18:46:06Z
项目社区:https://github.com/inaka/zipper

开源协议:Apache License 2.0

下载


Zipper GitHub Actions CI

Generic zipper implementation in Erlang.

Zippers: what are they good for?

Zippers let you traverse immutable data structures with ease and flexibility.

Contact Us

If you find any bugs or have a problem while using this library, please
open an issue in this repo (or a pull request :)).

And you can check all of our open-source projects at inaka.github.io

Usage

For a map tree structure like the following:

  1. Root = #{type => planet,
  2. attrs => #{name => "Earth"},
  3. children => [
  4. #{type => continent,
  5. attrs => #{name => "America"},
  6. children => [
  7. #{type => country,
  8. attrs => #{name => "Argentina"},
  9. children => []},
  10. #{type => country,
  11. attrs => #{name => "Brasil"},
  12. children => []}
  13. ]
  14. },
  15. #{type => continent,
  16. attrs => #{name => "Europe"},
  17. children => [
  18. #{type => country,
  19. attrs => #{name => "Sweden"},
  20. children => []},
  21. #{type => country,
  22. attrs => #{name => "England"},
  23. children => []}
  24. ]
  25. }
  26. ]
  27. },

You can build a zipper by providing three simple functions:

  • IsBranchFun: takes a node and returns true if it is a branch node or
    false otherwise.
  • ChildrenFun: takes a node and returns a list of its children.
  • MakeNodeFun: takes a node and a list of children and returns a new node
    containing the supplied list as children.

This is an example of how you would define a zipper and then use it to traverse
the map tree structure above:

  1. %% Create the zipper
  2. IsBranchFun = fun
  3. (#{children := [_ | _]}) -> true;
  4. (_) -> false
  5. end,
  6. ChildrenFun = fun(Node) -> maps:get(children, Node) end,
  7. MakeNodeFun = fun(Node, Children) -> Node#{children => Children} end,
  8. Zipper = zipper:new(fun is_map/1, ChildrenFun, MakeNodefun, Root),
  9. %% Traverse the zipper with next
  10. Zipper1 = zipper:next(Zipper),
  11. Zipper2 = zipper:next(Zipper),
  12. %% Get the current zipper node
  13. Argentina = zipper:node(Zipper2).
  14. io:format("~p", [Argentina]),
  15. %%= #{type => country,
  16. %%= attrs => #{name => "Argentina"},
  17. %%= children => []}
  18. %% Go up and get the node
  19. Zipper3 = zipper:up(Zipper2).
  20. America = zipper:node(Zipper2).
  21. io:format("~p", [America]),
  22. %%= #{type => country,
  23. %%= attrs => #{name => "America"},
  24. %%= children => [#{...}, #{...}]}

Tests

Circular dependency in test environment (Katana Test ->
Elvis Core -> Zipper) is
fixed by including Zipper as a dep in the test profile in rebar.config

  1. ...
  2. {profiles, [
  3. {test, [
  4. {deps, [
  5. %% The tag will be replaced by the rebar.config.script
  6. {zipper, {git, "https://github.com/inaka/zipper.git", {tag, "irrelevant"}}},
  7. ...
  8. ]}
  9. ]}
  10. ]}.
  11. ...

but then, we still replace the tag with the current branch. This is done in rebar.config.script.
Therefore, it’s really important to have the branch updated and pushed to github before running the
tests with rebar3 ct.

References