2015-07-10 22 views
5

Sto cercando di implementare un tipo GF seguendo lo schizzo di Andreas Noack nella sua conferenza di Stanford del 2015, ma ho qualche problema all'inizio. Sto usando Julia 0.3.10julia parametrico costruttore - problemi con il costruttore esterno

suo codice rilevante è la seguente:

# 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) 

Così il problema si presenta quando si tenta e proprio definire qualcosa di simile

GF{2}(11) 

Si ottiene

tipo non può essere costruito

Ok, quindi non esiste un costruttore automatico.

GF{2,Int64}(11) funziona correttamente.

Il problema è che nessun costruttore automatico ha altre funzioni (come zero (x)) non riuscito.

I tentativi di fare un costruttore esterno non hanno lavorato per me:

Penso GF{P}(x::Integer) = GF{P,Int64}(x) dovrebbe funzionare, ma ho

Attenzione: static parametro P non si verifica in firma per GF a in [4]: ​​1. Il metodo non può essere richiamato.

Fondamentalmente io sono a corto di idee su come specificare che una chiamata come GF {3} (x) dovrebbe creare un'istanza di GF {3, typeof (x)} (x)

So che mi manca qualcosa di accecantemente ovvio.

Grazie

risposta

5

Sfortunatamente, questo non è possibile in 0.3. La possibilità di sovraccaricare lo call è una delle nuove grandi funzionalità che sarà disponibile in 0.4. Questa funzione è necessaria per chiamare un tipo non parametrizzato come GF{2}. Sembra che Andreas stia usando una versione 0.4-dev per questa demo; se vuoi seguire la sua demo direttamente, ti consiglio di fare lo stesso.


Più informazioni e un work-around:

In 0.3, si chiama il costruttore interiore di un tipo semplicemente chiamando il tipo - ma è necessario essere un concreto (o [foglia]) tipo. Ciò significa che deve essere completamente parametrizzato se ha parametri di tipo. In questo caso, significa che dovrai specificare manualmente il tipo intero per chiamare il costruttore interno: GF{2,Int}(5).

È inoltre possibile definire i costruttori esterni, che hanno l'aspetto e il comportamento proprio come una funzione generica che ha lo stesso nome di base. Puoi anche aggiungere parametri di tipo alle funzioni generiche, ma mentre sembrano simili ai parametri di un tipo (specialmente quando i nomi sono uguali), si comportano in modo molto diverso! I parametri di una funzione stanno definendo una variabile locale che verrà utilizzata per confrontarsi con i tipi degli argomenti.Ecco perché la tua definizione GF{P}(x::Integer) = GF{P,Int64}(x) lancia questa avvertenza: dal momento che non usi mai P per definire i tipi di argomento, Julia non sarà in grado di capire che cosa dovrebbe essere P e quindi non sarà mai richiamabile. Potremmo creare una funzione che fa sempre un GF{2}:

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) 

Nota che non ha specificato quali T dovrebbe essere quando ho chiamato GF2 - Julia capito che fuori. Questo crea confusione quando si definisce un costruttore esterno per un tipo parametrizzato, dal momento che GF{P}(x::Integer) = … è questo punto di sovrapposizione in cui il parametro della funzione e il parametro del tipo sembrano uguali! Il parametro function vince, e così mentre è possibile definire un costruttore esterno con parametri, quei parametri significano qualcosa di diverso e si chiama il costruttore esterno senza di essi: GF(…). Puoi chiamare il costruttore interno, ma devi specificare tutti i parametri: GF{2, Int}(…). Non c'è via di mezzo in 0.3.

Questo cambiamento in 0.4: Ora è possibile definire cosa succede quando si chiama un oggetto arbitrario! Ecco cosa sta definendo function call{P}(::Type{GF{P}}, x::Integer): se chiami il incompleto tipo GF{2}, questo metodo verrà chiamato con P=2. Di fatto, questo generalizza i costruttori esterni. Un costruttore esterno (anche se parametrizzato) è semplicemente uno "zucchero" per la definizione di call(::Type{GF}, x::Integer) per il tipo GF senza alcun parametro. Quindi questo è il modo in cui 0.4 consente tutti i tipi di comportamenti eccezionali con sovraccarico delle chiamate.


Se davvero si vuole fare questo lavoro a 0,3, si potrebbe o definire funzioni come GF2 superiore a quello hard-code il valore P, o si potrebbe fare il tipo meno flessibile:

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

Ora il costruttore interno è esattamente quello che volevi: GF{P}, quindi puoi chiamare direttamente GF{2}(5). Ma hai perso la flessibilità nel tipo intero che viene utilizzato. Ci sono anche altri trucchi, ma è per un'altra volta.

+0

Grazie mille, questo è stato aiutato enormemente, il passaggio al ramo di sviluppo sembra essere la strada da percorrere ... – Robin

+0

@Robin Se ritieni che questa risposta (eccellente) risponda alla tua domanda, ti preghiamo di indicare questo facendo clic sul segno di spunta successivo alla risposta e considerare anche l'up-voting. Saluti. –