2016-06-07 10 views
5

Sono abbastanza nuovo in F # e voglio modellare le cose nel mondo reale che hanno relazioni "ha-a" piuttosto complesse. Al vertice della gerarchia sono quattro tipi, A - D, con queste relazioni:Modellazione di gerarchie complesse in F #

A 
| 
+--A 
| 
+--B 
| | 
| +--B 
| | 
| +--D 
|  | 
|  +--D 
| 
+--C 
| | 
: +--D 
|  | 
|  +--D 
: 

Così tipo B può avere un "genitore" di A o B, e il tipo di D possono avere un genitore di B, C o D.

voglio usare sindacati discriminati per vincolare il genitore di ogni tipo, in modo che non può essere assegnato a non validi i genitori, ad esempio:

type B_Parent = A | B 
type D_Parent = B | C | D 

allora voglio utilizzare i record per modellare ogni tipo, in cui un campo è il genitore, ad esempio:

type A = { parent:A_Parent; ... } 
type B = { parent:B_Parent; ... } 
type C = { parent:C_Parent; ... } 
type D = { parent:D_Parent; ... } 

C_Parent non è un problema perché il suo genitore di tipo A è dichiarato in anticipo. E ho usato 'A option' per A_Parent. Ma non sono stato in grado di capire come definire B_Parent e D_Parent, a causa della loro dipendenza nidificata su se stessi e su altri tipi?

risposta

4

In primo luogo, una cosa molto importante: quando si scrive

type B_Parent = A | B 

siete non dichiarando che B_Parent è un DU che unisce i due tipi precedentemente definiti A e B. Non c'è sintassi per questo.

Ciò che la riga precedente sta effettivamente facendo è la definizione di due sottotipi vuoti di B_Parent, che non hanno nulla a che fare con l'originale A e B. E 'equivalente a:

type B_Parent = B_Parent.A | B_Parent.B 

Al fine di riutilizzare i tipi esistenti all'interno di un DU, si necessità di dare un nome a caso - in modo efficace avvolgendoli in un altro tipo. Quindi la dichiarazione corretta può essere qualcosa di simile:

type B_Parent = P_a of A | P_b of B 

Con che a parte - come ha detto Anton, la parola chiave per definire i tipi reciprocamente referenziale è and. Detto questo, è meglio mantenere riferimenti così reciproci quanto più piccoli e rigidi possibile. Così abbiamo potuto fare qualcosa di simile:

type A = { parent:A_Parent; ... } 
    and A_Parent = P_a of A 
       | None 

type B = { parent:B_Parent; ... } 
    and B_Parent = P_a of A 
       | P_b of B 

type C = { parent:C_Parent; ... } 
    and C_Parent = P_a of A 

type D = { parent:D_Parent; ... } 
    and D_Parent = P_b of B 
       | P_c of C 
       | P_d of D 

Abbiamo potuto utilizzare solo A option per A_Parent, e solo per AC_Parent, ma penso che mantenere i nomi di casi coerente probabilmente rendere le cose più leggibile.

+0

Grazie per la risposta, l'ho implementata e funziona perfettamente. Ma ho un altro problema. – DenisV

+0

A volte devo modificare un record o spostarlo su un altro genitore - operazione facile. Ma poiché i record F # sono immutabili, questo crea un nuovo record, ma mi rimane ancora quello originale in memoria.E non so come sbarazzarmene? – DenisV

+0

Perché hai bisogno di liberartene? Lascia che il netturbino lo pulisca quando non viene più utilizzato. – piaste

2

È possibile utilizzare la parola chiave and per definire i tipi che sono reciprocamente collegati, ad esempio:

type B_Parent = A | B 
and D_Parent = B | C | D 
and A_Parent = A 
and C_Parent = B 
and A = { parent:A_Parent } 
and B = { parent:B_Parent } 
and C = { parent:C_Parent } 
and D = { parent:D_Parent } 

Vedi anche questo related SO question. La parola chiave and è anche utile quando si definisce mutually recursive functions.