typename{...}(...)
(notez la partie {}
) Est un constructeur interne"?explicit inner constructor
?methods
pour vérifier si une méthode est un constructeur interne/externe?BTW, je sais comment utiliser et quand utiliser un constructeur interne. Je savais ce qu'est un constructeur intérieur jusqu'à ce que les constructeurs extérieurs uniquement entrent et brouillent les eaux. :(
Rappelons quelques déclarations du doc :
1. Méthodes du constructeur externe
Un constructeur est comme toute autre fonction dans Julia en ce que son comportement global est défini par le comportement combiné de ses méthodes.
2. Méthodes du constructeur interne
Une méthode de constructeur interne ressemble beaucoup à une méthode de constructeur externe, avec deux différences: 1. Elle est déclarée à l'intérieur du bloc d'une déclaration de type, plutôt qu'à l'extérieur comme des méthodes normales. 2. Il a accès à une fonction spéciale existant localement appelée
new
qui crée des objets du type du bloc.3. Constructeurs paramétriques
Sans constructeur interne explicitement fourni, la déclaration du type composite
Point{T<:Real}
Fournit automatiquement un constructeur interne,Point{T}
, Pour chaque type possibleT<:Real
, Qui se comporte exactement comme non paramétrique les constructeurs internes par défaut le font. Il fournit également un seul constructeur Point externe général qui prend des paires d'arguments réels, qui doivent être du même type.
J'ai trouvé que inner constructor methods
Ne peut pas être directement observé par methods
, même methods(Foo{Int})
fonctionne, ce n'est en fait pas "comme n'importe quelle autre fonction", les fonctions génériques courantes ne peuvent pas être methods
ed de cette manière.
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」
Cependant, le constructeurs externes uniquement ajoute une autre ride à l'histoire du constructeur:
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
IN dans un bloc de déclaration de type, et il appelle également new
. Je suppose que le but ici est juste d'empêcher Julia de définir la paire de constructeurs intérieur-extérieur par défaut pour nous, mais la deuxième déclaration de la documentation est-elle toujours vraie dans ce cas? C'est déroutant pour les nouveaux utilisateurs.
Ici , j'ai lu une autre forme de constructeurs internes:
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
C'est loin de la forme canonique Foo{T}(x::T) where {T} = new(x)
mais il semble que les résultats soient tout à fait les mêmes.
Donc ma question est quelle est la définition précise des constructeurs internes? Dans Julia-v0.6 +, est-il juste de dire que "tout constructeur qui peut être appelé avec la signature typename{...}(...)
(notez la partie {}
) Est un constructeur interne"?
À titre d'exemple, supposons que vous souhaitiez définir un type pour représenter des nombres pairs:
Julia> struct Even
e::Int
end
Julia> Even(2)
Even(2)
Jusqu'ici tout va bien, mais vous voulez également que le constructeur rejette les nombres impairs et jusqu'à présent, Even(x)
ne:
Julia> Even(3)
Even(3)
Vous essayez donc d'écrire votre propre constructeur comme
Julia> Even(x) = iseven(x) ? Even(x) : throw(ArgumentError("x=$x is odd"))
Even
et ... roulement de tambour, s'il vous plaît ... Cela ne fonctionne pas:
Julia> Even(3)
Even(3)
Pourquoi? Demandons à Julia ce qu'elle vient d'appeler:
Julia> @which Even(3)
Even(e::Int64) in Main at REPL[1]:2
Ce n'est pas la méthode que vous avez définie (regardez le nom de l'argument et le type), c'est le constructeur implicitement fourni. Peut-être qu'on devrait redéfinir ça? Eh bien, n'essayez pas cela à la maison:
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)
Nous venons de créer une boucle infinie: nous avons redéfini Even(e)
pour s'appeler récursivement. Nous sommes maintenant confrontés à un problème de poulet et d'oeuf: nous voulons redéfinir le constructeur implicite, mais nous avons besoin d'un autre constructeur pour appeler la fonction définie. Comme nous l'avons vu, appeler Even(e)
n'est pas une option viable.
La solution est de définir un constructeur interne:
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
..
Dans un constructeur interne, vous pouvez appeler le constructeur implicite d'origine à l'aide de la syntaxe new()
. Cette syntaxe n'est pas disponible pour les constructeurs externes. Si vous essayez de l'utiliser, vous obtiendrez une erreur:
Julia> Even() = new(2)
Even
Julia> Even()
ERROR: UndefVarError: new not defined
..