2015-07-10 14 views
5

Ich versuche, einen GF-Typ nach Andreas Noacks Skizze in seiner Stanford-Vorlesung 2015 zu implementieren, habe aber einige Probleme früh. Ich bin mit Julia 0.3.10Julia parametrische Konstruktor - Probleme mit äußeren Konstruktor

Sein entsprechenden Code lautet wie folgt:

# Scalar finite fields 
immutable GF{P,T<:Integer} <: Number 
    data::T 
    function GF(x::Integer) 
     return new(mod(x, P)) 
    end 
end 

    # methods for scalar finite field 
import Base: convert, inv, one, promote_rule, show, zero 

function call{P}(::Type{GF{P}}, x::Integer) 
    if !isprime(P) 
     throw(ArgumentError("P must be a prime")) 
    end 
    return GF{P,typeof(x)}(mod(x, P)) 
end 
convert{P,T}(::Type{GF{P,T}}, x::Integer) = GF{P}(x) 
convert{P}(::Type{GF{P}}, x::Integer) = GF{P}(x) 
convert{P,T}(::Type{GF{P,T}}, x::GF{P}) = GF{P,T}(x.data) 
promote_rule{P,T1,T2<:Integer}(::Type{GF{P,T1}}, ::Type{T2}) = GF{P,promote_type(T1,T2 
)} 
show(io::IO, x::GF) = show(io, x.data) 

So das Problem erscheint, wenn Sie versuchen, und nur etwas zu definieren, wie diese

GF{2}(11) 

Sie erhalten

Typ kann nicht konstruiert werden

Okay, es gibt also keinen automatischen Konstruktor.

GF{2,Int64}(11) funktioniert gut.

Das Problem ist, mit keinem automatischen Konstruktor andere Funktionen (wie Null (x)) fehlschlagen.

Versuche, einen externen Konstruktor zu machen, haben nicht für mich gearbeitet:

denke ich GF{P}(x::Integer) = GF{P,Int64}(x) arbeiten sollte, aber ich bekommen

Warnung: statische Parameter P in Unterschrift erfolgen für GF nicht an In [4]: ​​1. Die Methode kann nicht aufgerufen werden.

Grundsätzlich Ich laufe aus Ideen, wie zu spezifizieren, dass ein Anruf wie GF {3} (x) sollte eine Instanz von GF erstellen {3, typeof (x)} (x)

Ich weiß, dass mir etwas blendend offensichtlich fehlt.

Danke

Antwort

5

Leider ist dies bei 0,3 einfach nicht möglich. Die Fähigkeit, call zu überlasten, ist eine der großen neuen Funktionen, die in 0.4 verfügbar sein werden. Diese Funktion wird benötigt, um einen unvollständig parametrisierten Typ wie GF{2} aufzurufen. Es sieht so aus, als ob Andreas eine 0.4-dev-Version für diese Demo verwendet hätte; Wenn Sie seiner Demo direkt folgen möchten, würde ich Ihnen empfehlen, das gleiche zu tun.


Mehr Details und eine Behelfslösung:

In 0,3, rufen Sie einen inneren Konstrukteurs-Typ, indem Sie einfach die Art Aufruf - aber es muss ein Beton (oder [Blatt]) Typ sein. Das bedeutet, dass es vollständig parametrisiert werden muss, wenn es Typparameter hat. In diesem Fall müssen Sie den Integer-Typ manuell angeben, um den inneren Konstruktor aufzurufen: GF{2,Int}(5).

Sie können auch äußere Konstruktoren definieren, die aussehen und sich wie eine generische Funktion verhalten, die denselben Basisnamen hat. Sie können auch generischen Funktionen Typparameter hinzufügen, aber während sie den Parametern eines Typs ähneln (besonders wenn die Namen identisch sind), verhalten sie sich sehr unterschiedlich! Die Parameter einer Funktion definieren eine lokale Variable, die verwendet wird, um mit den Typen der Argumente übereinzustimmen.Deshalb gibt Ihre Definition GF{P}(x::Integer) = GF{P,Int64}(x) diese Warnung aus: Da Sie niemals P verwenden, um die Argumenttypen zu definieren, wird Julia nicht in der Lage sein herauszufinden, was P sein sollte, und so wird es nie aufrufbar sein. Wir könnten eine Funktion erstellen, die immer eine GF{2} macht:

julia> GF2{T}(x::T) = GF{2,T}(x) # Call the fully parameterized inner constructor 
GF2 (generic function with 1 method) 

julia> GF2(3) 
GF{2,Int64}(1) 

Bitte beachte, dass ich nicht angeben, was T sein sollte, wenn ich GF2 genannt - Julia dachte, dass aus. Dies wird nur verwirrend, wenn Sie einen äußeren Konstruktor für einen parametrisierten Typ definieren, da GF{P}(x::Integer) = … dieser überlappende Punkt ist, an dem der Funktionsparameter und der Typparameter gleich aussehen! Der Funktionsparameter gewinnt, und obwohl Sie einen äußeren Konstruktor mit Parametern definieren können, bedeuten diese Parameter etwas anderes und Sie rufen den äußeren Konstruktor ohne sie auf: . Sie können den inneren Konstruktor aufrufen, aber dort müssen Sie alle Parameter angeben: GF{2, Int}(…). Es gibt keinen Mittelweg in 0,3.

Dies ändert sich in 0.4: Jetzt können Sie definieren, was passiert, wenn Sie ein beliebiges Objekt aufrufen! Das ist, was function call{P}(::Type{GF{P}}, x::Integer) definiert: Wenn Sie die unvollständig Typ GF{2} aufrufen, wird diese Methode mit P=2 aufgerufen. In der Tat verallgemeinert dies äußere Konstruktoren. Ein äußerer Konstruktor (selbst wenn er parametrisiert ist) ist einfach ein "Zucker" zum Definieren von call(::Type{GF}, x::Integer) für den Typ GF ohne irgendwelche Parameter. So ermöglicht 0.4 alle möglichen großartigen Verhaltensweisen mit Call Overloading.


Wenn Sie wirklich diese Arbeit auf 0,3 bis machen möchten, können Sie entweder Funktionen definieren wie GF2 über das hart-Code den P Wert, oder Sie können die Art weniger flexibel machen:

immutable GF{P} <: Number 
    data::Int 
    function GF(x::Integer) 
     return new(mod(convert(Int, x), P)) 
    end 
end 

Jetzt ist der innere Konstruktor genau das, was Sie wollten: GF{P}, so können Sie GF{2}(5) direkt anrufen. Aber Sie haben die Flexibilität in dem Integer-Typ verloren, der verwendet wird. Es gibt noch andere Tricks, aber das ist für eine andere Zeit.

+0

Vielen Dank, das hat enorm geholfen, Umzug in den Entwicklungszweig scheint der Weg zu sein ... – Robin

+0

@Robin Wenn Sie glauben, dass (ausgezeichnete) Antwort Ihre Frage beantwortet, dann geben Sie dies bitte durch Klicken auf das Häkchen neben zur Antwort und erwäge auch Up-Voting. Prost. –

Verwandte Themen