这个问题看起来非常类似于在Julia中用递归调用减少JIT时间
为了回答这个问题,我将会对那里给出的代码进行改编、修正和阐述。
首先是一些定义:
abstract type BiTree{T} end
struct Branch{T} <: BiTree{T}
left::BiTree{T}
right::BiTree{T}
end
struct Leaf{T} <: BiTree{T}
value::T
end
Base.foldl(f, b::Branch) = f(foldl(f, b.left), foldl(f, b.right))
Base.foldl(f, l::Leaf) = f(l.value)
(⊕)(l::Branch, r::Branch) = Branch(l, r)
(⊕)(l, r::Branch) = Branch(Leaf(l), r)
(⊕)(l::Branch, r) = Branch(l, Leaf(r))
(⊕)(l, r) = Branch(Leaf(l), Leaf(r))
这里有一个抽象类型和两个子类型,一个用于树中的内部节点(组合类型),一个用于叶子节点。我们还有一个递归操作,包含两行定义,用于定义如何将树中的值进行折叠或缩减,并有一个简洁明了的中缀构造函数用于创建树。
如果我们定义了my_tree
并使用加法进行折叠,那么得到的结果如下:
julia> my_tree = ((((6 ⊕ 7) ⊕ (6 ⊕ 7)) ⊕ ((7 ⊕ 7) ⊕ (0 ⊕ 7))) ⊕ (((8 ⊕ 7) ⊕ (7 ⊕ 7)) ⊕ ((8 ⊕ 8) ⊕ (8 ⊕ 0)))) ⊕ ((((2 ⊕ 4) ⊕ 7) ⊕ (6 ⊕ (0 ⊕ 5))) ⊕ (((6 ⊕ 8) ⊕ (2 ⊕ 8)) ⊕ ((2 ⊕ 1) ⊕ (4 ⊕ 5))));
julia> typeof(my_tree)
Branch{Int64}
julia> foldl(+, my_tree)
160
请注意,
my_tree
的类型完全暴露了它是具有某种子节点的内部节点,但我们无法真正看到它有多深。我们不会得到像
Branch{Branch{Leaf{Int32},Branch{...
这样的类型。使用
BiTree{Int64}
可以看出
Branch{Int64}
是一个双重树形结构。
julia> isa(my_tree, BiTree{Int64})
true
但仅从my_tree
的值中并不能看出它的深度,而类型信息也没有深度的显示。
如果我们查看到目前为止生成的方法,会看到如下内容:
julia> using MethodAnalysis
julia> methodinstances(⊕)
4-element Vector{Core.MethodInstance}:
MethodInstance for ⊕(::Branch{Int64}, ::Branch{Int64})
MethodInstance for ⊕(::Int64, ::Branch{Int64})
MethodInstance for ⊕(::Branch{Int64}, ::Int64)
MethodInstance for ⊕(::Int64, ::Int64)
julia> methodinstances(foldl)
3-element Vector{Core.MethodInstance}:
MethodInstance for foldl(::typeof(+), ::Branch{Int64})
MethodInstance for foldl(::typeof(+), ::Leaf{Int64})
MethodInstance for foldl(::typeof(+), ::BiTree{Int64})
无论我们尝试构建什么32位整数的树,我们都只需要这些。无论我们尝试用
+
缩小哪棵树,我们也只需要这些。
如果我们尝试使用不同的运算符进行折叠,就可以得到更多的方法。
julia> foldl(max, my_tree)
8
julia> methodinstances(foldl)
6-element Vector{Core.MethodInstance}:
MethodInstance for foldl(::typeof(+), ::Branch{Int64})
MethodInstance for foldl(::typeof(max), ::Branch{Int64})
MethodInstance for foldl(::typeof(+), ::Leaf{Int64})
MethodInstance for foldl(::typeof(max), ::Leaf{Int64})
MethodInstance for foldl(::typeof(+), ::BiTree{Int64})
MethodInstance for foldl(::typeof(max), ::BiTree{Int64})
有趣的是,方法数量增长但不会爆炸式增长。