Si desidera attraversare l'intera struttura dati e modificare alcuni elementi qua e là. Questo di solito è fatto da una funzione che assume la struttura dati come parametro e restituisce la nuova versione modificata della struttura.
Per ogni caso di input, questa funzione definisce il modo in cui il nuovo valore che viene restituito dovrebbe apparire come .
La funzione di base che modifica uno Tree
(che è solo un elenco di valori DataA
) probabilmente dovrebbe solo restituire un nuovo elenco di valori modificati. Se rimandiamo quei modifiche dei valori da una funzione modifyA
, la funzione principale di modifica appare così:
-- # function to change a |Tree|
mutate :: Tree -> Tree
mutate as = map mutateA as
-- # (The |map| function applies the |mutateA| function to every
-- # element of |as|, creating a list of all the return values)
Ora la funzione mutateA
deve essere definito per cambiare tutti i possibili DataA
valori, e meglio è accompagnato da una funzione mutateB
che gestisce i valori DataB
.
Queste funzioni guardano i diversi casi possibili di valori e restituiscono i appropriate nuovi valori:
-- # function to change |DataA| items
mutateA :: DataA -> DataA
-- # A |DataA1| is a |DataA1| with modified values
mutateA (DataA1 bs) = DataA1 (map mutateB bs)
-- # A |DataA3| is a |DataA3| with modified values
mutateA (DataA3 s as) = DataA3 s (map mutateA as)
-- # In the remaining case(s) the value stays the same
mutateA d = d
-- # function to change |DataB| items
mutateB :: DataB -> DataB
mutateB (DataB1 as) = DataB1 (map mutateA as)
mutateB (DataB3 s bs) = DataB3 s (map mutateB bs)
-- # Here comes a real change
mutateB (DataB2 _) = DataB2 "foo"
In questo modo per ogni elemento nella struttura di un nuovo elemento viene calcolato, dove tutti i DataB2
valori ovunque nell'albero sono sostituiti da "pippo".
È relativamente dettagliato perché si hanno cinque casi diversi che contengono un elenco di valori che devono essere attraversati, ma che non è specifico di Haskell. In una lingua imperativa si dovrebbero avere in genere cinque cicli in sostituzione delle cinque chiamate a map
.
Forse potresti semplificare la struttura dei dati per ridurre questo "overhead".Questo dipende ovviamente dal tuo caso d'uso effettivo, ma forse, ad esempio, non hai bisogno dei casi Data2
C'è una differenza tra DataA2 "abc"
e DataA3 "abc" []
?
Grazie. Questo è quello che stavo cercando. Per far funzionare tutto questo, ho dovuto importare Data.Generics – Chris
Hai ragione, mio male! Risolto il problema con la mia risposta Grazie. – Martijn
Brillante. Sono contento che qualcuno qui sappia come far funzionare SYB. +1 –