@@ -230,9 +230,9 @@ function count_nodes(tree::AbstractNode; break_sharing=Val(false))
230230end
231231
232232"""
233- foreach(f::Function, tree::AbstractNode)
233+ foreach(f::Function, tree::AbstractNode; break_sharing::Val=Val(false) )
234234
235- Apply a function to each node in a tree.
235+ Apply a function to each node in a tree without returning the results .
236236"""
237237function foreach (
238238 f:: F , tree:: AbstractNode ; break_sharing:: Val = Val (false )
@@ -244,7 +244,7 @@ function foreach(
244244end
245245
246246"""
247- filter_map(filter_fnc::Function, map_fnc::Function, tree::AbstractNode, result_type::Type)
247+ filter_map(filter_fnc::Function, map_fnc::Function, tree::AbstractNode, result_type::Type, break_sharing::Val=Val(false) )
248248
249249A faster equivalent to `map(map_fnc, filter(filter_fnc, tree))`
250250that avoids the intermediate allocation. However, using this requires
@@ -286,20 +286,25 @@ function filter_map!(
286286end
287287
288288"""
289- filter(f::Function, tree::AbstractNode)
289+ filter(f::Function, tree::AbstractNode; break_sharing::Val=Val(false) )
290290
291291Filter nodes of a tree, returning a flat array of the nodes for which the function returns `true`.
292292"""
293293function filter (f:: F , tree:: AbstractNode ; break_sharing:: Val = Val (false )) where {F<: Function }
294294 return filter_map (f, identity, tree, typeof (tree); break_sharing)
295295end
296296
297+ """
298+ collect(tree::AbstractNode; break_sharing::Val=Val(false))
299+
300+ Collect all nodes in a tree into a flat array in depth-first order.
301+ """
297302function collect (tree:: AbstractNode ; break_sharing:: Val = Val (false ))
298303 return filter (Returns (true ), tree; break_sharing)
299304end
300305
301306"""
302- map(f::Function , tree::AbstractNode, result_type::Type{RT}=Nothing)
307+ map(f::F , tree::AbstractNode, result_type::Type{RT}=Nothing; break_sharing::Val=Val(false)) where {F<:Function,RT}
303308
304309Map a function over a tree and return a flat array of the results in depth-first order.
305310Pre-specifying the `result_type` of the function can be used to avoid extra allocations.
@@ -314,6 +319,11 @@ function map(
314319 end
315320end
316321
322+ """
323+ count(f::F, tree::AbstractNode; init=0, break_sharing::Val=Val(false)) where {F<:Function}
324+
325+ Count the number of nodes in a tree for which the function returns `true`.
326+ """
317327function count (
318328 f:: F , tree:: AbstractNode ; init= 0 , break_sharing:: Val = Val (false )
319329) where {F<: Function }
@@ -327,22 +337,44 @@ function count(
327337 ) + init
328338end
329339
340+ """
341+ sum(f::Function, tree::AbstractNode; init=0, return_type=Undefined, f_on_shared=_default_shared_aggregation, break_sharing::Val=Val(false)) where {F<:Function}
342+
343+ Sum the results of a function over a tree. For graphs with shared nodes
344+ such as `GraphNode`, the function `f_on_shared` is called on the result
345+ of each shared node. This is used to avoid double-counting shared nodes (default
346+ behavior).
347+ """
330348function sum (
331349 f:: F ,
332350 tree:: AbstractNode ;
333351 init= 0 ,
334352 return_type= Undefined,
335- f_on_shared= (c, is_shared) -> is_shared ? ( false * c) : c ,
353+ f_on_shared= _default_shared_aggregation ,
336354 break_sharing:: Val = Val (false ),
337355) where {F<: Function }
338356 if preserve_sharing (typeof (tree))
339357 @assert typeof (return_type) != = Undefined " Must specify `return_type` as a keyword argument to `sum` if `preserve_sharing` is true."
340358 end
341359 return tree_mapreduce (f, + , tree, return_type; f_on_shared, break_sharing) + init
342360end
361+ function _default_shared_aggregation (c, is_shared)
362+ return is_shared ? (false * c) : c
363+ end
343364
365+ """
366+ all(f::Function, tree::AbstractNode)
367+
368+ Reduce a flag function over a tree, returning `true` if the
369+ function returns `true` for all nodes, `false` otherwise.
370+ """
344371all (f:: F , tree:: AbstractNode ) where {F<: Function } = ! any (t -> ! @inline (f (t)), tree)
345372
373+ """
374+ mapreduce(f::Function, op::Function, tree::AbstractNode; return_type, f_on_shared, break_sharing)
375+
376+ Map a function over a tree and aggregate the result using an operator `op`.
377+ """
346378function mapreduce (
347379 f:: F ,
348380 op:: G ,
@@ -369,28 +401,34 @@ function length(tree::AbstractNode; break_sharing::Val=Val(false))
369401 return count_nodes (tree; break_sharing)
370402end
371403
372- function hash (tree:: AbstractExpressionNode{T} ) where {T}
404+ """
405+ hash(tree::AbstractExpressionNode{T}[, h::UInt]; break_sharing::Val=Val(false)) where {T}
406+
407+ Compute a hash of a tree. This will compute a hash differently
408+ if nodes are shared in a tree. This is ignored if `break_sharing` is set to `Val(true)`.
409+ """
410+ function hash (
411+ tree:: AbstractExpressionNode{T} , h:: UInt = zero (UInt); break_sharing:: Val = Val (false )
412+ ) where {T}
373413 return tree_mapreduce (
374- t -> t. constant ? hash ((0 , t. val:: T )) : hash ((1 , t. feature)),
375- t -> hash ((t. degree + 1 , t. op)),
376- (n... ) -> hash (n),
414+ t -> t. constant ? hash ((0 , t. val:: T ), h ) : hash ((1 , t. feature), h ),
415+ t -> hash ((t. degree + 1 , t. op), h ),
416+ (n... ) -> hash (n, h ),
377417 tree,
378- UInt64 ;
418+ UInt ;
379419 f_on_shared= (cur_hash, is_shared) ->
380- is_shared ? hash ((:shared , cur_hash)) : cur_hash,
420+ is_shared ? hash ((:shared , cur_hash), h) : cur_hash,
421+ break_sharing,
381422 )
382423end
383424
384425"""
385- copy_node(tree::AbstractExpressionNode)
426+ copy_node(tree::AbstractExpressionNode; break_sharing::Val=Val(false) )
386427
387428Copy a node, recursively copying all children nodes.
388429This is more efficient than the built-in copy.
389430
390- id_map is a map from `objectid(tree)` to `copy(tree)`.
391- We check against the map before making a new copy; otherwise
392- we can simply reference the existing copy.
393- [Thanks to Ted Hopp.](https://stackoverflow.com/questions/49285475/how-to-copy-a-full-non-binary-tree-including-loops)
431+ If `break_sharing` is set to `Val(true)`, sharing in a tree will be ignored.
394432"""
395433function copy_node (
396434 tree:: N ; break_sharing:: Val = Val (false )
@@ -409,12 +447,20 @@ function copy_node(
409447 )
410448end
411449
450+ """
451+ copy(tree::AbstractExpressionNode; break_sharing::Val=Val(false))
452+
453+ Copy a node, recursively copying all children nodes.
454+ This is more efficient than the built-in copy.
455+
456+ If `break_sharing` is set to `Val(true)`, sharing in a tree will be ignored.
457+ """
412458function copy (tree:: AbstractExpressionNode ; break_sharing:: Val = Val (false ))
413459 return copy_node (tree; break_sharing)
414460end
415461
416462"""
417- convert(::Type{AbstractExpressionNode{T1}}, n::AbstractExpressionNode{T2}) where {T1,T2}
463+ convert(::Type{<: AbstractExpressionNode{T1}}, n::AbstractExpressionNode{T2}) where {T1,T2}
418464
419465Convert a `AbstractExpressionNode{T2}` to a `AbstractExpressionNode{T1}`.
420466This will recursively convert all children nodes to `AbstractExpressionNode{T1}`,
0 commit comments