constructor julia-lang

¿Qué diablos es un constructor interno?



julia-lang (1)

TL; DR:

  1. ¿Cuál es la definición precisa de constructores internos? En Julia-v0.6 +, ¿es correcto decir que "cualquier constructor al que se pueda llamar con el nombre typename{...}(...) la firma typename{...}(...) (note que la parte {} ) es un constructor interno"?
  2. Como se explica en el comentario a continuación, ¿es el constructor sólo externo un explicit inner constructor ?
  3. ¿Es correcto usar methods para verificar si un método es un constructor interno / externo?
  4. ¿Cuál es la diferencia entre los constructores predeterminados que definen automáticamente Julia y los correspondientes definidos explícitamente por los usuarios?

Por cierto, sé cómo usar y cuándo usar un constructor interno. Sabía lo que es un constructor interno hasta que los constructores sólo externos entran y enturbian las aguas. :(

Recordemos algunas declaraciones del doc :

1. Métodos de construcción externos

Un constructor es como cualquier otra función en Julia en que su comportamiento general se define por el comportamiento combinado de sus métodos.

2. Métodos de construcción interna

Un método de constructor interno es muy similar a un método de constructor externo, con dos diferencias: 1. Se declara dentro del bloque de una declaración de tipo, en lugar de hacerlo fuera de él como los métodos normales. 2. Tiene acceso a una función especial existente localmente llamada new que crea objetos del tipo de bloque.

3. Constructores paramétricos

Sin ningún constructor interno provisto explícitamente, la declaración del tipo compuesto Point{T<:Real} proporciona automáticamente un constructor interno, Point{T} , para cada tipo posible T<:Real , que se comporta como constructores internos predeterminados no paramétricos hacer. También proporciona un único constructor de punto externo general que toma pares de argumentos reales, que deben ser del mismo tipo.

Descubrí que inner constructor methods no pueden ser observados directamente por los methods , incluso los methods(Foo{Int}) funcionan, en realidad no es "como cualquier otra función", las funciones genéricas comunes no pueden ser methods editados de esta manera.

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」

Sin embargo, los constructores solo externos agregan otra arruga a la historia del constructor:

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

Hmmm, un outer constructor EN un bloque de declaración de tipo, y también llama new . Supongo que el propósito aquí es simplemente evitar que Julia defina el par de constructores interno-externo predeterminado para nosotros, pero ¿la segunda afirmación de la documentación sigue siendo cierta en este caso? Es confuso para los nuevos usuarios.

Here , leí otra forma de constructores internos:

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

Está lejos de la forma canónica Foo{T}(x::T) where {T} = new(x) pero parece que los resultados son muy similares.

Entonces, mi pregunta es ¿cuál es la definición precisa de constructores internos? En Julia-v0.6 +, ¿es correcto decir que "cualquier constructor al que se pueda llamar con el nombre typename{...}(...) la firma typename{...}(...) (note que la parte {} ) es un constructor interno"?


A modo de ejemplo, suponga que desea definir un tipo para representar números pares:

julia> struct Even e::Int end julia> Even(2) Even(2)

Hasta ahora todo bien, pero también desea que el constructor rechace números impares y hasta ahora Even(x) no:

julia> Even(3) Even(3)

Así que intentas escribir tu propio constructor como

julia> Even(x) = iseven(x) ? Even(x) : throw(ArgumentError("x=$x is odd")) Even

y ... tambor rollo, por favor ... no funciona:

julia> Even(3) Even(3)

¿Por qué? Preguntémosle a Julia cómo acaba de llamar:

julia> @which Even(3) Even(e::Int64) in Main at REPL[1]:2

Este no es el método que usted definió (mire el nombre del argumento y el tipo), este es el constructor provisto implícitamente. Tal vez deberíamos redefinir eso? Bueno, no intentes esto en casa:

julia> Even(e::Int) = iseven(e) ? Even(e) : throw(ArgumentError("e=$e is odd")) Even julia> Even(2) ERROR: Error: Stacktrace: [1] Even(::Int64) at ./REPL[11]:0 [2] Even(::Int64) at ./REPL[11]:1 (repeats 65497 times)

Acabamos de crear un bucle infinito: redefinimos Even(e) para que se llame a sí mismo recursivamente. Ahora estamos enfrentando un problema de huevo y gallina: queremos redefinir el constructor implícito, pero necesitamos algún otro constructor para llamar a la función definida. Como hemos visto llamar a Even(e) no es una opción viable.

La solución es definir un constructor interno:

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

Dentro de un constructor interno, puede llamar al constructor implícito original usando la sintaxis new() . Esta sintaxis no está disponible para los constructores externos. Si intentas usarlo, obtendrás un error:

julia> Even() = new(2) Even julia> Even() ERROR: UndefVarError: new not defined ..