web-dev-qa-db-ja.com

内部のコンストラクタは一体何ですか?

TL; DR:

  1. 内部コンストラクタの正確な定義は何ですか? Julia-v0.6 +では、「シグニチャーtypename{...}(...)(_{}_の部分に注意)で呼び出すことができるコンストラクターはすべて内部コンストラクターである」と言ってもよろしいですか?
  2. 以下のコメントで説明されているように、外部専用コンストラクターは実際には_explicit inner constructor_ですか?
  3. メソッドが内部/外部コンストラクタであるかどうかを確認するためにmethodsを使用することは正しいですか?
  4. Juliaによって自動的に定義されたデフォルトのコンストラクターと、ユーザーによって明示的に定義された対応するコンストラクターの違いは何ですか?

ところで、内部コンストラクタをいつどのように使用するかは知っています。 外側のみのコンストラクタ が入り、水を濁らせるまで、内側のコンストラクタが何であるかを知っていました。 :(

doc からいくつかのステートメントを思い出してみましょう:

1.外部コンストラクターメソッド

コンストラクタは、Juliaの他の関数と同じように、その全体的な動作は、メソッドの組み合わせた動作によって定義されます。

2.内部コンストラクターメソッド

内部コンストラクターメソッドは、外部コンストラクターメソッドによく似ていますが、次の2つの違いがあります。1.通常のメソッドのようにブロックの外側ではなく、型宣言のブロック内で宣言されます。 2.ブロックのタイプのオブジェクトを作成するnewと呼ばれるローカルに存在する特別な関数にアクセスできます。

3.パラメトリックコンストラクター

明示的に提供された内部コンストラクターがない場合、複合タイプ_Point{T<:Real}_の宣言は、可能な各タイプ_Point{T}_の内部コンストラクター_T<:Real_を自動的に提供します。これは、ノンパラメトリックなデフォルトの内部コンストラクターのように動作します。行う。また、同じタイプの実引数のペアを取る単一の一般的な外部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」
_

ただし、 外側のみのコンストラクター は、コンストラクターストーリーにもう1つのしわを追加します。

_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がデフォルトの内部と外部のコンストラクタペアを定義しないようにすることだけだと思いますが、この場合、ドキュメントの2番目のステートメントはまだ当てはまりますか?新しいユーザーを混乱させるものです。

ここ 、別の形式の内部コンストラクターを読み取ります。

_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{...}(...)(_{}_の部分に注意)で呼び出すことができるコンストラクターはすべて内部コンストラクターである」と言ってもよろしいですか?

32
Gnimuc

例として、偶数を表すタイプを定義するとします。

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