typename{...}(...)
调用的构造函数(注意{}
部分)是内部构造函数”?explicit inner constructor
?methods
检查方法是否为内/外构造函数是否正确?BTW,我知道如何使用以及何时使用内部构造函数。我知道内部构造函数是什么,直到outer-only constructors进入并混淆了水域。 :(
让我们回顾一下doc的一些陈述:
1。外部构造方法
构造函数与Julia中的任何其他函数一样,其整体行为由其方法的组合行为定义。
2。内部构造方法
内部构造函数方法很像外部构造函数方法,有两个不同之处:1。它在类型声明的块中声明,而不是像普通方法一样在它之外声明。 2.它可以访问一个名为
new
的特殊本地存在函数,该函数创建块类型的对象。3。参数化构造函数
如果没有任何显式提供的内部构造函数,复合类型
Point{T<:Real}
的声明会自动为每个可能的类型Point{T}
提供内部构造函数T<:Real
,其行为与非参数类似默认的内部构造函数。它还提供了一个通用的外部Point构造函数,它接受一对真实参数,它们的类型必须相同。
我发现inner constructor methods
无法直接观察到methods
,即使methods(Foo{Int})
有效,它实际上也不是“就像任何其他函数一样”,常见的泛型函数不能{{1以这种方式编辑。
methods
然而,outer-only constructors为构造函数故事添加了另一个皱纹:
julia> struct Foo{T}
x::T
end
julia> methods(Foo)
# 2 methods for generic function "(::Type)":
(::Type{Foo})(x::T) where T in Main at REPL[1]:2 # outer ctor 「1」
(::Type{T})(arg) where T in Base at sysimg.jl:24 # default convertion method「2」
julia> @which Foo{Int}(1) # or methods(Foo{Int})
(::Type{Foo{T}})(x) where T in Main at REPL[1]:2 # inner ctor 「3」
嗯,类型声明块中的julia> struct SummedArray{T<:Number,S<:Number}
data::Vector{T}
sum::S
function SummedArray(a::Vector{T}) where T
S = widen(T)
new{T,S}(a, sum(S, a))
end
end
julia> methods(SummedArray)
# 2 methods for generic function "(::Type)":
(::Type{SummedArray})(a::Array{T,1}) where T in Main at REPL[1]:5 # outer ctor「4」
(::Type{T})(arg) where T in Base at sysimg.jl:24
,它也会调用outer constructor
。我想这里的目的只是为了防止Julia为我们定义默认的内外构造函数对,但是在这种情况下,文档中的第二个语句仍然是真的吗?这对新用户来说很困惑。
Here,我读了另一种形式的内部构造函数:
new
它远离规范形式julia> struct Foo{T}
x::T
(::Type{Foo{T}})(x::T) = new{T}(x)
end
julia> methods(Foo)
# 1 method for generic function "(::Type)":
(::Type{T})(arg) where T in Base at sysimg.jl:24
julia> methods(Foo{Int})
# 2 methods for generic function "(::Type)":
(::Type{Foo{T}})(x::T) where T in Main at REPL[2]:3 「5」
(::Type{T})(arg) where T in Base at sysimg.jl:24
但似乎结果完全相同。
所以我的问题是内部构造函数的准确定义是什么?在Julia-v0.6 +中,是否正确地说“任何可以使用签名Foo{T}(x::T) where {T} = new(x)
调用的构造函数(注意typename{...}(...)
部分)是内部构造函数”?
答案 0 :(得分:8)
举例来说,假设您要定义一个表示偶数的类型:
julia> struct Even
e::Int
end
julia> Even(2)
Even(2)
到目前为止一切顺利,但你也希望构造函数拒绝奇数,到目前为止Even(x)
不会:
julia> Even(3)
Even(3)
因此,您尝试将自己的构造函数编写为
julia> Even(x) = iseven(x) ? Even(x) : throw(ArgumentError("x=$x is odd"))
Even
和...鼓声,请...它不起作用:
julia> Even(3)
Even(3)
为什么呢?让我们问朱莉娅她刚才叫什么:
julia> @which Even(3)
Even(e::Int64) in Main at REPL[1]:2
这不是您定义的方法(查看参数名称和类型),这是隐式提供的构造函数。也许我们应该重新定义它?好吧,不要在家里试试这个:
julia> Even(e::Int) = iseven(e) ? Even(e) : throw(ArgumentError("e=$e is odd"))
Even
julia> Even(2)
ERROR: StackOverflowError:
Stacktrace:
[1] Even(::Int64) at ./REPL[11]:0
[2] Even(::Int64) at ./REPL[11]:1 (repeats 65497 times)
我们刚创建了一个无限循环:我们重新定义Even(e)
以递归调用自身。我们现在面临鸡和蛋的问题:我们想要重新定义隐式构造函数,但是我们需要一些其他构造函数来调用已定义的函数。我们已经看到调用Even(e)
不是一个可行的选择。
解决方案是定义内部构造函数:
julia> workspace()
julia> struct Even
e::Int
Even(e::Int) = iseven(e) ? new(e) : throw(ArgumentError("e=$e is odd"))
end
julia> Even(2)
Even(2)
julia> Even(3)
ERROR: ArgumentError: e=3 is odd
..
在内部构造函数中,您可以使用new()
语法调用原始隐式构造函数。外部构造函数无法使用此语法。如果您尝试使用它,则会收到错误消息:
julia> Even() = new(2)
Even
julia> Even()
ERROR: UndefVarError: new not defined
..