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

时间:2017-07-26 13:46:06

标签: constructor julia

TL; DR:

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

1 个答案:

答案 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 
..