@@ -412,7 +412,6 @@ function Base.iterate(ti::StatelessBFS, ind)
412412end
413413
414414
415- # TODO : still experimenting here
416415"""
417416 MapNode{T,C}
418417
@@ -428,13 +427,13 @@ struct MapNode{T,C}
428427 value:: T
429428 children:: C
430429
431- # TODO : need a way to modify children but hard to think of a good general way of doing it...
432- function MapNode (𝒻, node)
433- v = 𝒻 (node)
434- ch = map (c -> MapNode (𝒻, c), children (node))
435- new {typeof(v),typeof(ch)} (v, ch)
430+ function MapNode (f, node)
431+ (v, ch) = f (node)
432+ ch′ = map (c -> MapNode (f, c), ch)
433+ new {typeof(v),typeof(ch′)} (v, ch′)
436434 end
437435end
436+ MapNode (node) = MapNode (n -> (nodevalue (n), children (n)), nodevalue (node))
438437
439438children (μ:: MapNode ) = μ. children
440439
@@ -452,31 +451,51 @@ end
452451Base. show (io:: IO , :: MIME"text/plain" , μ:: MapNode ) = print_tree (io, μ)
453452
454453
455- # TODO : still experimenting here
456454"""
457455 treemap(f, node)
458456
459- Apply the function `f` to every node in the tree with root `node`. `node` must satisfy the AbstractTrees interface.
460- Instead of returning the result of `f(n)` directly the result will be a tree of [`MapNode`](@ref) objects isomorphic
461- to the original tree but with values equal to the corresponding `f(n)`.
457+ Apply the function `f` to every node in the tree with root `node`, where `f` is a function that returns `(v, ch)` where
458+ `v` is a new value for the node (i.e. as returned by [`nodevalue`](@ref) and `ch` is the new children of the node.
459+ `f` will be called recursively so that all the children returned by `f` for a parent node will again be called for
460+ each child. This means that to maintain the structure of a tree but merely map to new values, one should define
461+ `f = node -> (g(node), children(node))` for some function `g` which returns a value for the node.
462+
463+ The nodes of the output tree will all be represented by [`MapNode`](@ref) objects wrapping the returned values.
464+ This is necessary in order to guarantee that the output types can describe any tree topology.
462465
463466Note that in most common cases tree nodes are of a type which depends on their connectedness and the function
464467`f` should take this into account. For example the tree `[1, [2, 3]]` contains integer leaves but two
465468`Vector` nodes. Therefore, the `f` in `treemap(f, [1, [2,3]])` must be a function that is valid for either
466469`Int` or `Vector`. Alternatively, to only operate on leaves do `map(𝒻, Leaves(itr))`.
467470
468- ## Example
471+ It's very easy to write an `f` that makes `treemap` stack-overflow. To avoid this, ensure that `f` eventually
472+ termiantes, i.e. that sometimes it returns empty `children`. For example, if `f(n) = (nothing, [0; children(n)])` will
473+ stack-overflow because every node will have at least 1 child.
474+
475+ ## Examples
469476```julia
470477julia> t = [1, [2, 3]];
471478
472- julia> f(x ) = x isa AbstractArray ? nothing : x + 1;
479+ julia> f(n ) = n isa AbstractArray ? ( nothing, children(n)) : (n+1, children(n))
473480
474481julia> treemap(f, t)
475- MapNode{Nothing, MapNode}(nothing)
476- ├─ MapNode{Int64, MapNode{Union{}}}(2)
477- └─ MapNode{Nothing, MapNode{Int64, MapNode{Union{}}}}(nothing)
478- ├─ MapNode{Int64, MapNode{Union{}}}(3)
479- └─ MapNode{Int64, MapNode{Union{}}}(4)
482+ nothing
483+ ├─ 2
484+ └─ nothing
485+ ├─ 3
486+ └─ 4
487+
488+ julia> g(n) = isempty(children(n)) ? (nodevalue(n), []) : (nodevalue(n), [0; children(n)])
489+ g (generic function with 1 method)
490+
491+ julia> treemap(g, t)
492+ Any[1, [2, 3]]
493+ ├─ 0
494+ ├─ 1
495+ └─ [2, 3]
496+ ├─ 0
497+ ├─ 2
498+ └─ 3
480499```
481500"""
482501treemap (f, node) = MapNode (f, node)
0 commit comments