2013-01-21 14 views
5

Sto provando a scrivere un pacchetto in Go che calcola un'equazione usando un tipo "generico". Per essere precisi, voglio implementare l'approssimazione runge kutta 5.Vai (lingua) tipo/interfaccia numerico generale

Questa approssimazione calcola il valore di una funzione (sconosciuto) y nel punto t0 + h utilizzando solo il valore di y a t0, l'ora di inizio t0, la larghezza passo h e un'equazione differenziale dgl che è della forma dy/dt = g(t,y) dove g è una funzione.

Questa approssimazione si comporta esattamente allo stesso modo quando si lavora con tipi scalari come quando si lavora con vettori (o anche matrici). Più in generale: Funziona con tutto ciò che può essere aggiunto/sottratto ad un valore dello stesso tipo e può essere scalato da uno scalare (per il quale uso float64)

Così ho provato ad esprimere questo come un'interfaccia Go:

type Numeric interface { 
    Add(rhs Numeric) Numeric 
    Sub(rhs Numeric) Numeric 
    Mul(rhs float64) Numeric 
} 

Ma quando provo a "implementare" questa interfaccia, mi sono imbattuto in difficoltà a causa dei parametri di tipo:

type Vec6F struct { 
    x, y, z float64 
    vx, vy, vz float64 
} 

func (lhs *Vec6F) Add(rhs *Vec6F) rk5.Numeric { 
    result := new(Vec6F) 
    result.x = lhs.x + rhs.x 
    result.y = lhs.y + rhs.y 
    result.z = lhs.z + rhs.z 
    result.vx = lhs.vx + rhs.vx 
    result.vy = lhs.vy + rhs.vy 
    result.vz = lhs.vz + rhs.vz 
    return result 
} 

questo mi dà l'errore

cannot use result (type *Vec6F) as type rk5.Numeric in return argument: 
     *Vec6F does not implement rk5.Numeric (wrong type for Add method 
       have Add(*Vec6F) rk5.Numeric 
       want Add(rk5.Numeric) rk5.Numeric 

che è, da un lato assolutamente logica a me (perché dx potrebbe essere un altro oggetto che implementa numerico)

Ma d'altra parte: Come faccio a esprimere qualcosa di simile a Go? In C++ potrei usare invece l'overloading dell'operatore, ma questo non è possibile in go.

+0

"off-topic": l'equazione differenziale risolta è quella che descrive l'orbita di Mar (semplice). Per il codice completo quick'n'dirty vedi http://play.golang.org/p/GwehylBbLK –

risposta

2

Per essere generico, il metodo Add deve avere un parametro Numeric. Il modo normale per affrontare questo è con un tipo di affermazione come questo (on playground)

func (lhs *Vec6F) Add(_rhs Numeric) Numeric { 
    result := new(Vec6F) 
    rhs := _rhs.(*Vec6F) // type assertion - will panic if wrong type passes 
    result.x = lhs.x + rhs.x 
    result.y = lhs.y + rhs.y 
    result.z = lhs.z + rhs.z 
    result.vx = lhs.vx + rhs.vx 
    result.vy = lhs.vy + rhs.vy 
    result.vz = lhs.vz + rhs.vz 
    return result 
} 

Si potrebbe anche usare un sensore a se aveste diversi tipi si voleva per la conversione tra.

+0

Thx, questa è probabilmente la soluzione con cui finirò, anche se non mi piacciono le asserzioni di tipo. Si converte in un tipo di puntatore invece di '_rhs. (Vec6F)' per proteggere una copia non necessaria, giusto? –

+0

Doveva essere un tipo di puntatore per far funzionare il tipo assertion. Prova a cambiarlo sul link del parco giochi qui sopra per vedere cosa intendo - riceverai l'errore "impossibile tipo di asserzione: Vec6F non implementa Numeric (il metodo Add richiede il ricevitore del puntatore)" –

1

In effetti, i generici non sono supportati in go. Se si desidera che un tipo implementi un'interfaccia, i prototipi dei metodi devono corrispondere esattamente: è necessario func (lhs *Vec6F) Add(rhs Numeric) Numeric.

Ecco un tentativo di scrivere questo metodo che utilizza un tipo di asserzione:

func (lhs *Vec6F) Add(rhs Numeric) Numeric { 
    vrhs := rhs.(*Vec6F) 
    result := new(Vec6F) 
    result.x = lhs.x + vrhs.x 
    result.y = lhs.y + vrhs.y 
    result.z = lhs.z + vrhs.z 
    result.vx = lhs.vx + vrhs.vx 
    result.vy = lhs.vy + vrhs.vy 
    result.vz = lhs.vz + vrhs.vz 
    return result 
} 

Compila e dovrebbe funzionare quando viene chiamato con il diritto tipi di argomento, tuttavia, direi che si tratta di un abuso.

Nulla impedisce (con l'eccezione di un errore di runtime) di utilizzare questo metodo per aggiungere vettori agli scalari poiché entrambi implementerebbero Numeric. Alla fine, non otterresti nulla dall'uso dell'astrazione dell'interfaccia.

In questo caso, la filosofia go impone l'utilizzo di metodi/funzioni specifici del tipo.

+2

Cosa intendi esattamente per "uso di metodi/funzioni specifici del tipo"? Puoi fare un esempio? –

+0

Come in, non si dispone di un Add generico che funzioni su Vec6F e altri tipi simili. Avresti addVec6F, ecc. –

0

Ci sono due problemi che stai incontrando.

1.) Il motivo per cui non viene compilato e si lamenta delle interfacce non corrispondenti è perché Vec6F non soddisfa la firma della funzione per rk5.Numeric. Entrambi restituiscono il valore e i parametri di input devono corrispondere al tipo.

http://play.golang.org/p/kc9V9EXxJq correzioni problema, ma ne crea uno nuovo ...

2.) a fare la partita metodo firme così firma soddisfa Vec6F di numerico si è rotto la capacità di eseguire operazioni numeriche sui valori di proprietà.Questo perché le interfacce hanno solo metodi, nessuna proprietà.

Nel vostro caso, sarebbe logico che l'interfaccia numerica fornisse un metodo di accesso che restituirebbe un array di matrice che il ricevitore eseguirà quindi l'addizione | sub | Ciò potrebbe complicare ciò che deve essere fatto all'interno dei metodi di implementazione di ogni interfaccia, ma penso che ti porterebbe ciò che stai cercando.

+1

Perché avevo bisogno dei risultati (del calcolo) circa 2 ore fa, avevo cambiato il codice per usare fette di float64 invece, che è in qualche modo una versione radicale di fornire un tale metodo di accesso. Il codice completo è http://play.golang.org/p/GwehylBbLK - Funziona, ma a mio parere è molto lontano dall'essere un bel codice. Il problema con il metodo accessor è il seguente: Supponiamo di dover usare numeri complessi. O vorrei solo valori di interi in un altro caso d'uso. Fornire una soluzione di accesso diventa quindi probabilmente impossibile, poiché non potrei operare solo su vettori/matrici ma anche su polinomi. –