究竟什么是内部构造函数?

Gni*_*muc 30 constructor julia

TL; DR:

  1. 内部构造函数的准确定义是什么?在Julia-v0.6 +中,是否正确地说"任何可以使用签名调用的构造函数typename{...}(...)(注意{}部分)是内部构造函数"?
  2. 正如下面的评论所讨论的那样,外部构造函数实际上是一个explicit inner constructor
  3. 那么,是否可以使用methods用于检查方法是否是内部/外部构造?
  4. 由Julia自动定义的默认构造函数与用户明确定义的相应构造函数之间的区别是什么?

顺便说一句,我知道如何使用以及何时使用内部构造函数.我知道内部构造函数是什么,直到外部构造者进入并混淆了水域.:(

让我们回顾一下来自该文档的一些陈述:

1.外部构造方法

构造函数与Julia中的任何其他函数一样,其整体行为由其方法的组合行为定义.

2.内部构造方法

内部构造函数方法很像外部构造函数方法,有两个不同之处:1.它在类型声明的块中声明,而不是像普通方法一样在它之外声明.2.它可以访问一个特殊的本地存在的函数new,该函数创建块类型的对象.

3.参数化构造函数

如果没有任何显式提供的内部构造函数,复合类型的声明会Point{T<:Real}自动Point{T}为每个可能的类型提供一个内部构造函数T<:Real,其行为就像非参数默认内部构造函数一样.它还提供了一个总的外点构造函数,对真正的参数,它必须是同一类型的.

我发现inner constructor methods不能直接观察methods,甚至是methods(Foo{Int})作品,它实际上并不"像任何其他功能一样",常见的通用功能不能methods以这种方式编辑.

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?
Run Code Online (Sandbox Code Playgroud)

但是,外部构造函数为构造函数故事添加了另一个皱纹:

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
Run Code Online (Sandbox Code Playgroud)

嗯,一个outer constructorIN类型声明块,它也调用 new.我想这里的目的只是为了防止Julia为我们定义默认的内外构造函数对,但是在这种情况下,文档中的第二个语句仍然是真的吗?这对新用户来说很困惑.

在这里,我读了另一种形式的内部构造函数:

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
Run Code Online (Sandbox Code Playgroud)

它远离规范形式,Foo{T}(x::T) where {T} = new(x)但似乎结果完全相同.

所以我的问题是内部构造函数的准确定义是什么?在Julia-v0.6 +中,是否正确地说"任何可以使用签名调用的构造函数typename{...}(...)(注意{}部分)是内部构造函数"?

Ale*_*sky 10

举例来说,假设您要定义一个表示偶数的类型:

julia> struct Even
          e::Int
       end

julia> Even(2)
Even(2)
Run Code Online (Sandbox Code Playgroud)

到目前为止一切顺利,但你也希望构造函数拒绝奇数,到目前为止Even(x)还没有:

julia> Even(3)
Even(3)
Run Code Online (Sandbox Code Playgroud)

因此,您尝试将自己的构造函数编写为

julia> Even(x) = iseven(x) ? Even(x) : throw(ArgumentError("x=$x is odd"))
Even
Run Code Online (Sandbox Code Playgroud)

和...鼓声,请...它不起作用:

julia> Even(3)
Even(3)
Run Code Online (Sandbox Code Playgroud)

为什么?让我们问朱莉娅她刚刚打来的电话:

julia> @which Even(3)
Even(e::Int64) in Main at REPL[1]:2
Run Code Online (Sandbox Code Playgroud)

这不是您定义的方法(查看参数名称和类型),这是隐式提供的构造函数.也许我们应该重新定义它?好吧,不要在家里试试:

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) 
Run Code Online (Sandbox Code Playgroud)

我们刚刚创建了一个无限循环:我们重新定义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
..
Run Code Online (Sandbox Code Playgroud)

在内部构造函数中,您可以使用new()语法调用原始隐式构造函数.外部构造函数无法使用此语法.如果您尝试使用它,您将收到错误:

julia> Even() = new(2)
Even

julia> Even()
ERROR: UndefVarError: new not defined 
..
Run Code Online (Sandbox Code Playgroud)