Cosa sono le funzioni ricorsiveLe funzioni ricorsive sono quelle funzioni che al loro interno hanno una chiamata a se stesse.
Di primo impatto può sembrare una cosa di poco senso destinata solo a loop infiniti e se scritte male cio è vero.
Se scritte correttamente invece si rivelano determinanti quando con esse si analizzano dati annidati come la lista delle sottodirectory di una directory.
L'algoritmo migliore per risolvere questo tipo di problemi è proprio quello di usare questo tipo di funzioni dato che ogni directory può avere n sottodirectory che a loro volta possono avere n sottodirectory etc...ed il numero di cicli annidati non lo si può decidere a priori poichè può variare da percorso a percorso.
Basta dare alla funzione ricorsiva parametri che possano ricrearsi all'interno della funzione stessa e chaimarla da un punto iniziale ed il gioco è fatto.
Questo metodo si applica in diverse situazioni in questo articolo ne vengono prese in esame 3 di utili.
Ricorsione su Directory e FilesPer l'esempio delle directory/files basta utilizzare la seguente funzione:
1) la funzione:
private void LeggiDir(string pDir,TreeNode pTrn)
{
foreach (string zDirs in System.IO.Directory.GetDirectories(pDir))
{
LeggiDir(zDirs, pTrn.Nodes.Add(zDirs.Remove(0,pDir.Length+1)));
}
foreach (string zFls in System.IO.Directory.GetFiles(pDir))
{
pTrn.Nodes.Add(zFls.Remove(0,pDir.Length+1));
}
}
2) chiamo la funzione ricorsiva con un nodo iniziale:
LeggiDir(@"c:\windows",miaTreeView.Nodes.Add (@"c:\windows"));
il trucco sta nel fatto che il metodo add dell'insieme nodes ritorna un nodo che a sua volta puo avere dei sottonodi percui ha il suo insieme nodes e via così.
Il ciclo si interrompe appena non ci sono sottodirectory infatti foreach non restituisce niente e quindi non viene richiamata se stessa internamente.
Se si prova a debuggare l'applicazione si rischia di impazzire, ma fortunatamete il computer si gestisce bene e sa sempre a che profondita è arrivato.
Il remove della stringa ritornata dal metodo getdirectories serve per lasciare solo il nome della directory o del file e non tutto il path.
Esempio in esecuzione sulla ricorsione di Directory
Ricorsione all'interno degli elementi di un file XMLUn caso simile è quello che si debba ciclare all'interno di un documento xml per ricreare la struttura:
per compattezza del codice si deve introdurre all'inizio la direttiva:
1) La funzione:
private void LeggiNodi(XPathNavigator pNav,TreeNode pNodo)
{
pNav.MoveToFirstChild();
do
{
switch (pNav.NodeType)
{
case XPathNodeType.Element:
LeggiNodi(pNav,pNodo.Nodes.Add("<" + pNav.LocalName + ">" ));
break;
case XPathNodeType.Text:
pNodo.Text += pNav.Value + pNodo.Text;
break;
}
} while (pNav.MoveToNext());
pNav.MoveToParent();
}
2) chiamo la funzione ricorsiva con un nodo iniziale:
XPathNavigator nav = new XPathDocument(txtXml.Text).CreateNavigator();
LeggiNodi(nav,miaTreeView.Nodes.Add("XMLroot"));
Anche qui il metodo è simile al precedente, in particolare viene utilizzata la classe Xpathnavigator per "navigare" tra i nodi di un documento xml.
In paticolare viene controllato il tipo di elemento restituito dal Xpathnavigator per sapere se si tratta del contenuto di un tag o di un elemento.
In questo ultimo caso viene preso il nome del nodo, altrimenti al nodo inserito nella chiamata ricorsiva precedente viene aggiunto il suo testo.
Particolare è il ritorno al parent che serve a riportare il puntatore al successivo nodo superiore.
Esempio in esecuzione sulla ricorsione all'interno di un file XML
Ricorsione sugli oggetti di una FormInfine un'altro metodo utile, soprattutto quando ci troviamo a dover pulire i filtri impostati da un'utente, è quello di ciclare su tutti i controlli di una form in questo modo:
1) la funzione:
private void pulisci(Control pCtrl)
{
foreach (Control zObj in pCtrl.Controls)
{
switch (zObj.GetType().Name)
{
case "TextBox":
zObj.Text = "";
break;
case "ListBox":
ListBox Lst = (ListBox)zObj;
Lst.Items.Clear();
break;
default:
if (zObj.Controls.Count > 0) pulisci(zObj);
break;
}
}
}
2) chiamo la funzione ricorsiva con un nodo iniziale:
Anche qui controlliamo che un controllo abbia controlli contenuti in esso altrimenti usciamo dal loop annidato.
ProLa pulizia del codice e la dinamicità ad adattarsi ai diversi contesti
ControBisogna progettarle bene per evitare loop infiniti e sono un po' complicate da debuggare, inquanto si rischia di perdere il punto dove si è arrivati.