2016-06-07 18 views
7

Sto considerando di scrivere un tipo simile a quelli definiti in NamedArrays e Images. Diciamo che voglio essenzialmente una matrice con un pezzo di metadati, diciamo un nome user-friendly che scriverò all'inizio di un file quando scriverò la matrice sul disco. (Questo dettaglio non è rilevante; sto solo escogitare un esempio.)Come evitare grandi quantità di piastre elettriche in Julia con nuovi tipi?

così potrei fare

type MyNamedArray 
    data::Array 
    name::ASCIIString 
end 

function mywrite(f,x::MyNamedArray) 
    write(f,x.name) 
    write(f,x.data) 
end 

o qualcosa del genere, e nessun altro comportamento deve essere diverso dal comportamento di base Array.

Nella mia testa, è "ovvio" che voglio solo ogni funzione esistente che opera su Array per operare sul campo data di questo tipo. In un'altra lingua, ad es. Java Avrei potuto creare una sottoclasse di array e aggiungere name come campo di istanza alla sottoclasse, che preserverebbe automaticamente la compatibilità con tutte le operazioni di array esistenti. Ma a Julia, se provo una soluzione come quella sopra, ora ho bisogno di definire altre tonnellate di funzioni, ad es. come @TimHoly e 'davidavdav' hanno fatto nei pacchetti collegati.

Naturalmente sono consapevole che essere costretti a scrivere alcune di queste funzioni a mano è utile per realizzare cose che non hai pensato. Per esempio. nell'esempio MyNamedArray di cui sopra, si potrebbe obiettare sottolineando che non ho definito il nome di x::MyNamedArray * y::MyNamedArray. Ma cosa succede se non mi interessa e voglio il codice che "funziona", senza tanto materiale? (Vedere per esempio un ciclo su simboli per spingere nuove definizioni metodo in NamedArrays e manualmente scrivendo una centinaia di linee di definizioni in Images. La maggior parte di queste definizioni sono boilerplate/definizione "ovvio".)

particolare per continuare l'esempio ho citato , per MyNamedArray, il valore predefinito potrebbe essere x*y non è più un MyNamedArray, cioè poiché ogni funzione ha appena impostato il comportamento "ereditato" dell'applicazione della stessa funzione sui dati sottostanti, possiamo semplicemente dimenticare i metadati su tutte le funzioni preesistenti.

Nota, trovo la risposta di Tomas Lycken here perspicace, e così sono la domanda e le risposte here.

La migliore sintesi che riesco a ottenere è "devi solo succhiarlo e scrivere le funzioni, o scrivere una macro che faccia questo per te". Se questo è il caso, così sia; Mi sto solo chiedendo se mi manca una soluzione migliore, in particolare un modo migliore per progettare la soluzione per renderlo più Julian ed evitare il boilerplate.

risposta

5

È possibile ottenere la maggior parte del tragitto semplicemente sottoclasse AbstractArray: http://docs.julialang.org/en/latest/manual/interfaces/#abstract-arrays. In effetti, è possibile fare uno migliore e sottoclasse DenseArray, che richiede inoltre la definizione di una funzione stride (e probabilmente pointer) ... che consente all'array personalizzato di funzionare con BLAS. Questa è solo una manciata di metodi che devi definire. Non è al 100% dal momento che molti autori hanno ancora la tendenza a limitare eccessivamente i metodi per accettare solo Array quando possono facilmente accettare tutti gli AbstractArrays. Questo è qualcosa che è migliorato significativamente negli ultimi due anni, e sta ancora migliorando.

In generale, uno schema che ho trovato molto utile qui è definire le interfacce in termini di supertipi astratti e allentare il più possibile le firme dei metodi. Se le restrizioni di spedizione non sono necessarie, è possibile consentire qualsiasi tipo e basarsi sulla digitazione anatra.Se si limita la spedizione a specifici tipi di foglia quando si dice a Julia come dovrebbe ciarlare o quando si appoggia alla sua implementazione interna, allora il lavoro diventa molto più estensibile e riutilizzabile.

+0

Un sacco di buoni suggerimenti qui; Mi armerò un po 'e riferirò. Grazie! – Philip

+0

Penso che ciò sollevi una domanda correlata sulla relazione logica tra l'implementazione di interfacce e la sottotipizzazione. In altre lingue con interfacce esplicite la distinzione mi è già chiara. In Julia è solo una distinzione "dispatch contro runtime"? In altre parole, se dichiari un tipo un sottotipo di un supertipo, i metodi definiti sul supertipo verranno inviati anche al sottotipo (a condizione che tu non li abbia sovrascritti). Ma poi l'implementazione di quei metodi potrebbe fallire in fase di esecuzione se non hai implementato l'interfaccia informale? – Philip

+0

Sì, è [digitazione anatra] (http://stackoverflow.com/questions/4205130/what-is-duck-typing). In realtà sposta l'errore solo di un livello - invece di lanciare un MethodError per 'sum (:: MyNamedArray)', si ottiene un MethodError quando tenta di iterare o indicizzare nell'array all'interno dell'implementazione della somma. È un errore di metodo mancante nel runtime in entrambi i casi. –

Problemi correlati