【问题标题】:What on earth is an inner constructor?到底什么是内部构造函数?
【发布时间】:2018-01-01 21:17:26
【问题描述】:

TL;DR:

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

顺便说一句,我知道如何使用以及何时使用内部构造函数。我知道什么是内部构造函数,直到outer-only constructors 进来搅乱了水。 :(

让我们回顾一下doc中的一些陈述:

1。外部构造方法

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

2。内部构造方法

内部构造方法很像外部构造方法,但有两个区别: 1. 它是在类型声明的块内声明的,而不是像普通方法那样在其外部声明。 2. 它可以访问名为new 的特殊本地存在函数,该函数创建块类型的对象。

3。参数构造函数

没有任何显式提供的内部构造函数,复合类型Point{T<:Real} 的声明自动为每个可能的类型T<:Real 提供一个内部构造函数Point{T},其行为就像非参数默认内部构造函数一样。它还提供了一个通用的外部 Point 构造函数,该构造函数接受一对必须是相同类型的实参。

我发现inner constructor methods不能被methods直接观察到,即使methods(Foo{Int})有效,它其实也不是“和其他任何函数一样”,普通的泛型函数不能这样methodsed。

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」

不过,outer-only constructors 给构造函数的故事又添了一笔:

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 IN 一个类型声明块,它也调用new。我想这里的目的只是为了防止 Julia 为我们定义默认的内部-外部构造函数对,但是在这种情况下,文档中的第二条语句仍然正确吗?这让新用户感到困惑。

Here,我看了另一种形式的内部构造函数:

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

这与Foo{T}(x::T) where {T} = new(x) 的规范形式相去甚远,但结果似乎完全一样。

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

【问题讨论】:

  • 我的 2¢ 是当您想要绕过默认的外部构造函数(无论是隐式还是显式)时使用内部构造函数,以便在返回实例之前执行初始化/测试等.当内部构造函数存在时,默认的外部构造函数不再适用,除非您明确定义一个。我不同意您在上面的某些示例中所谓的内部/外部构造函数(但这可能只是一个错字)。另请参阅this question(免责声明:我的)作为内部构造函数适用的示例。
  • 我认为这里的内部和外部之间的区别混淆了这个问题。问题更多是默认 vs 显式 vs 隐式 vs 专用 vs 泛化。该文档所说的是,当未提供显式内部构造函数时,存在等效于某些显式内部构造函数公式的默认构造函数。因此,我将 [1] 称为通用默认构造函数,[3] 称为专用默认构造函数,[4] 是显式内部构造函数(它也恰好是参数化的),[5] 也是如此(虽然,写得稍微有点复杂的方式)。
  • @TasosPapastylianou 是的,自动内部和外部构造函数的机制很重要(感谢您清理它),但事实上,我可以看到它在未来发生变化。内部构造函数分配和生成一致的结构,外部构造函数包装这些结构以提供各种创建方法的概念是定义的核心。从这个意义上说,我什至可以看到定义new 在结构定义之外调用内部构造函数的能力。也许甚至覆盖内部构造函数以对某些特定类型参数进行附加约束可能很方便
  • @Gnimuc 我同意,它可能更清楚。标题重点可能应该是关于可用的隐含默认内部(和外部)参数构造函数的范围,以及如果定义了显式内部构造函数,这些构造函数将如何不再适用。如果是这种情况,那么您只能依赖创建适当的外部构造函数,这些构造函数是显式内部构造函数的有效包装,并且您不能再依赖尚未激活的隐式构造函数,这一点很自然。跨度>
  • 感谢您的回复!我刚刚在这里github.com/JuliaLang/julia/issues/23022 提出了一个问题,让我们把讨论移到那里。

标签: constructor julia


【解决方案1】:

举个例子,假设你想定义一个类型来表示偶数:

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

【讨论】:

    猜你喜欢
    • 2017-12-08
    • 1970-01-01
    • 2014-06-02
    • 2011-04-16
    • 2011-10-23
    • 1970-01-01
    • 1970-01-01
    • 2020-12-14
    • 2023-03-23
    相关资源
    最近更新 更多