RISOLTO Questa funzione ricorsiva va bene?

Pubblicità

Hero467

Utente Attivo
Messaggi
695
Reazioni
406
Punteggio
75
Salve a tutti, per un progettino che sto scrivendo ho bisogno di utilizzare una funzione ricorsiva, ma siccome ancora non ho completa dimestichezza con queste non so se potrebbe generare un loop infinito o potenziali errori.

La funzione è questa:
Java:
private SingleContent getCurrent(String path) {
    SingleContent[] content = this.current.getContent();
    SingleContent curr = null;

    for (SingleContent element : content) {
        if (element.getPath().equals(path)) {
            curr = element;
        }
    }

    if (curr != null)
        return curr;

    for (SingleContent element : content) {
        curr = getCurrent(element.getPath());
    }

    return curr;
}

Mi scuso in anticipo se ho dimenticato qualche dettaglio oppure non ho spiegato bene qualcosa, ho programmato tutto il giorno e sono abbastanza fuso. In caso provvederò a mettere quello che manca al più presto
 
Salve a tutti, per un progettino che sto scrivendo ho bisogno di utilizzare una funzione ricorsiva, ma siccome ancora non ho completa dimestichezza con queste non so se potrebbe generare un loop infinito o potenziali errori.

La funzione è questa:
Java:
private SingleContent getCurrent(String path) {
    SingleContent[] content = this.current.getContent();
    SingleContent curr = null;

    for (SingleContent element : content) {
        if (element.getPath().equals(path)) {
            curr = element;
        }
    }

    if (curr != null)
        return curr;

    for (SingleContent element : content) {
        curr = getCurrent(element.getPath());
    }

    return curr;
}

Mi scuso in anticipo se ho dimenticato qualche dettaglio oppure non ho spiegato bene qualcosa, ho programmato tutto il giorno e sono abbastanza fuso. In caso provvederò a mettere quello che manca al più presto
Ad occhio credo sia errata.
Quello che fa sta funzione è loopare lungo content, se trova qualcosa aggiorna curr e non ritorna altrimenti loopa sull'elemento di content ricorsivamente finche non trova il tutto.
Non sono certo di cosa deve fare la funzione, ma sicuramente il doppio for è inutile.
Inoltre se nelle successive iterazioni potresti perdere il valore di curr.
Adesso se è un file è altamente improbabile che abbia lo stesso nome, a meno di ignorare i whitespace.
Inoltre se non trova il file?
 
Quello che fa sta funzione è loopare lungo content, se trova qualcosa aggiorna curr e non ritorna altrimenti loopa sull'elemento di content ricorsivamente finche non trova il tutto.
Il fatto che non ritorni nel loop è perché quello che sta cercando è il percorso di un file, quindi qualcosa di univoco. Teoricamente non dovrebbe succedere che curr venga riassegnato una seconda volta, perché la vedo dura trovare due file con lo stesso identico percorso. Quindi che curr venga ritornato nel ciclo o finito il ciclo la cosa non dovrebbe cambiare, ma potrei sbagliarmi

Non sono certo di cosa deve fare la funzione, ma sicuramente il doppio for è inutile.
La funzione va a lavorare su una serie di dati che altre classi hanno estrapolato da delle directory, quindi non su cartelle vere e proprie. Dati come nome e percorso.
Il doppio for l'avevo messo per fare in modo che, se non avesse trovato il percorso che cercava nella serie di cartelle fornita all'inizio, andasse a esaminare tutte le cartelle contenute nelle directory della lista iniziale, entrando poi nelle sotto-sotto cartelle e cosi via. Dovrebbe essere un algoritmo di ricerca in un B-tree teoricamente, ma non sono sicuro di averlo implementato correttamente

Inoltre se nelle successive iterazioni potresti perdere il valore di curr.
Come detto prima, questo non dovrebbe succedere, perché il percorso di una cartella è univoco, quindi non dovrebbe esserci il rischio di trovarne due uguali

Adesso se è un file è altamente improbabile che abbia lo stesso nome, a meno di ignorare i whitespace.
Inoltre se non trova il file?
Questa parte non l'ho capita
 
Io non credo di aver esattamente capito a cosa ti serve. Cos'è di preciso SingleContent?
La cosa che non ho capito è perchè cerchi se esiste e matcha il percorso (usando equals) torni il percorso, e se non esiste torni null.

Il codice ti serve per sapere solo se esiste quindi? In questo caso sarebbe più appropriata una funzione booleana. Se invece vuoi che ti venga restituito un file, potresti far tornare direttamente un oggetto che rappresenta il file (File).

Un'implementazione corretta, in pseudocodice, secondo me potrebbe essere qualcosa come:

Codice:
FUNC  exists(String directory, String path) {
    String[] paths = getFilesInDirectory(directory);

    FOR i < paths.length {
        if(paths[i].isDirectory()) {
            return exists(paths[i], path);
       }
       
       if(paths[i].equals(path)) {
           return true;
       }
    }
    return false;
}

Se l'obbiettivo è invece ottenere un File, allora ha senso restituire un File (o null) invece di un boolean.

La ccosa migliore però è appoggiarsi a ciò che già esiste in Java senza reinventare nulla: https://docs.oracle.com/javase/tutorial/essential/io/walk.html penso ti possa tornare utile.
 
Quindi che curr venga ritornato nel ciclo o finito il ciclo la cosa non dovrebbe cambiare, ma potrei sbagliarmi
il primo ciclo è terribile: metti che "content", qualunque cosa sia, abbia 10000 elementi, continui ad eseguire il ciclo inutilmente anche se trovi subito il "path"; al limite devi farlo con un while ed una condizione di uscita immediata
il secondo for è inutile
comunque a parte questa osservazione che lascia il tempo che trova, secondo me la cosa non va fatta con un ciclo ma con un if:
  • esegui il confronto
  • se il confronto va a buon fine ritorni il risultato altrimenti fai una chiamata ricorsiva sul prossimo elemento da esaminare
  • se non trova nulla l'ultima istruzione deve necessariamente ritornare null;
questo vale per una singola cartella, poi è eventualmente da aggiustare se ci sono più cartelle da esaminare
 
Io non credo di aver esattamente capito a cosa ti serve. Cos'è di preciso SingleContent?
La cosa che non ho capito è perchè cerchi se esiste e matcha il percorso (usando equals) torni il percorso, e se non esiste torni null.
SingleContent è la rappresentazione astratta di una cartella o un file (in questo caso cartella, perché i file sono stati rimossi), con solo le proprietà essenziali.
Però non torna il percorso, torna direttamente la cartella con quel percorso specifico (sempre rappresentata con SingleContent)

Se l'obbiettivo è invece ottenere un File, allora ha senso restituire un File (o null) invece di un boolean.

La ccosa migliore però è appoggiarsi a ciò che già esiste in Java senza reinventare nulla: https://docs.oracle.com/javase/tutorial/essential/io/walk.html penso ti possa tornare utile.
La classe File l'ho utilizzata in un altro contesto per ottenere questi dati, ma ho scelto di rappresentare file e cartelle con una classe custom invece di usare la classe File. Più flessibilità, e si trattava di un'implementazione di soltanto poche decine di righe di codice.

il primo ciclo è terribile: metti che "content", qualunque cosa sia, abbia 10000 elementi, continui ad eseguire il ciclo inutilmente anche se trovi subito il "path"; al limite devi farlo con un while ed una condizione di uscita immediata
il secondo for è inutile
Ok, questo non l'avevo considerato, perché davo per scontato che tutti i file system fossero come il mio con poche decine di elementi per directory. Correggo il codice

comunque a parte questa osservazione che lascia il tempo che trova, secondo me la cosa non va fatta con un ciclo ma con un if:
  • esegui il confronto
  • se il confronto va a buon fine ritorni il risultato altrimenti fai una chiamata ricorsiva sul prossimo elemento da esaminare
  • se non trova nulla l'ultima istruzione deve necessariamente ritornare null;
questo vale per una singola cartella, poi è eventualmente da aggiustare se ci sono più cartelle da esaminare
Quello che dicevi l'ho pensato anche io all'inizio, ma c'è il problema dell'analisi di più cartelle allo stesso "livello". Cerco di spiegarmi meglio:
mettiamo di avere una cartella contenente altre 3 cartelle, con ognuna delle quali contenente altre 3 cartelle. Il ciclo, togliendo la parte ricorsiva, andrebbe ad esaminare le 3 cartelle iniziali.
Ora, aggiungendo la parte ricorsiva, se non trova nulla nel primo elemento dell'array va ad esaminare il suo contenuto chiamando di nuovo la funzione, esaminando il primo elemento anche di quello e così via. Finite tutte le sotto-directory, sotto-sotto-directory ecc del primo elemento della lista si passa al secondo, grazie al ciclo for.
Dando per scontato che l'elemento esiste (perché di quello sono certo) e che è univoco (e sono certo anche di quello) il problema non è se esiste, ma dov'è.
Una volta che l'ha trovato lo ritorna e il resto del programma svolge le operazioni su di esso.




Facciamo che per chiarire eventuali altri dubbi dico anche cosa sto facendo: quella funzione fa parte di uno scanner del file system.
Il programma fa in maniera ricorsiva lo scan di tutte le directory, ne classifica il contenuto (se directory o file, e in caso di file che tipo) e rappresenta il tutto (precedentemente memorizzato in un array 2d) con un b-tree. SingleContent è un nodo dell'albero, e la funzione vi ho chiesto di guardare è un algoritmo di ricerca per trovare una specifica cartella (rappresentata da SingleContent) avendo il percorso, che è univoco
 
ho scelto di rappresentare file e cartelle con una classe custom invece di usare la classe File. Più flessibilità, e si trattava di un'implementazione di soltanto poche decine di righe di codice
non è una buona scelta
a parte che reinventi una cosa che è già presente, un path è una stringa (che può essere una cartella o un file);
non hai bisogno di cicli per esaminare un inseme di cartelle allo stesso livello, devi dire alla funzione ricorsiva
Codice:
if(il path è una cartella)
    chiama ricorsivamente la funzione entrando nella cartella
else
    esamina il primo file ed eventualmente chiama ricorsivamente la funzione sul resto dei file
questo significa cambiare l'ordine in cui esamini i singoli file, entrando prima nel livello di sottocartelle più profondo
 
In tutta onestà lascia perdere il concetto di utilizzare una classe per rappresentare un oggetto di utilizzo comune, anche se il programma è fine a se stesso è un pessima pratica. Ciò che puoi fare è ereditare qualcosa o ancor meglio utilizzare il principio di segregazione delle interfacce.
Tieni conto che le funzioni ricorsive sono molto comode ma per concetti di caching sarebbero da evitare come la peste, idem le liste.
In questo caso son comode, ma il for loop programmato così è davvero pessimo a livello di ottimizzazione,
Passagli il content come parametro, usalo per creare le condizioni di uscita e ti eviti i loop. Usa 2 funzioni, oppure usa il walk consigliato da @DispatchCode che sarebbe decisamente meglio.
 
Si, credo che farò il walk. A conoscerle prima queste classi, mi avrebbero risparmiato un sacco di lavoro
 
Si, credo che farò il walk. A conoscerle prima queste classi, mi avrebbero risparmiato un sacco di lavoro
Il 99% di quello che puoi fare in java e python è stato già implementato in una libreria.
La prossima volta son certo che controllerai online
 
C'è una cosa che non mi è chiara dalla doc: il file tree lo devo implementare io da 0 o è l'implementazione di FileVisitor?
 
C'è una cosa che non mi è chiara dalla doc: il file tree lo devo implementare io da 0 o è l'implementazione di FileVisitor?
Puoi implementare l'interfaccia, ma in quel caso dovrai definire tu i vari metodi.

Una cosa che puoi fare, perchè secondo me ti è sufficiente, è estendere la classe che viene mostrata anche li nell'esempio, SimpleFileVisitor.
Potresti provare anche l'esempio che vedi li sotto, magari capisci meglio se ti è utile o no.
 
Puoi implementare l'interfaccia, ma in quel caso dovrai definire tu i vari metodi.

Una cosa che puoi fare, perchè secondo me ti è sufficiente, è estendere la classe che viene mostrata anche li nell'esempio, SimpleFileVisitor.
Potresti provare anche l'esempio che vedi li sotto, magari capisci meglio se ti è utile o no.
Si, però gli esempi mi sembrano incompleti. Nel primo snippet dichiarano la classe statica, quindi si suppone sia dentro un'altra classe che faccia cose. Poi la classe BasicFileAttributes è li, ma non viene spiegata (o almeno, non mi sembra ci sia) e quindi non so cosa faccia e come usarla. Inoltre le costanti nel primo snippet tipo CONTINUE vengono spiegate più sotto.

In sostanza, mi sembra che questa doc sia la seconda parte di qualcos'altro
 
Si, però gli esempi mi sembrano incompleti. Nel primo snippet dichiarano la classe statica, quindi si suppone sia dentro un'altra classe che faccia cose. Poi la classe BasicFileAttributes è li, ma non viene spiegata (o almeno, non mi sembra ci sia) e quindi non so cosa faccia e come usarla. Inoltre le costanti nel primo snippet tipo CONTINUE vengono spiegate più sotto.

In sostanza, mi sembra che questa doc sia la seconda parte di qualcos'altro

No, l'esempio è funzionante. Va aggiunto qualche pezzo, come gli import. Io ho rimosso lo static, ho fatto 1 file unico per farti l'esempio al volo:

Java:
import static java.nio.file.FileVisitResult.*;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.io.IOException;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;


  class PrintFiles
    extends SimpleFileVisitor<Path> {

    // Print information about
    // each type of file.
    @Override
    public FileVisitResult visitFile(Path file,
                                   BasicFileAttributes attr) {
        if (attr.isSymbolicLink()) {
            System.out.format("Symbolic link: %s ", file);
        } else if (attr.isRegularFile()) {
            System.out.format("Regular file: %s ", file);
        } else {
            System.out.format("Other: %s ", file);
        }
        System.out.println("(" + attr.size() + "bytes)");
        return CONTINUE;
    }

    // Print each directory visited.
    @Override
    public FileVisitResult postVisitDirectory(Path dir,
                                          IOException exc) {
        System.out.format("Directory: %s%n", dir);
        return CONTINUE;
    }

    // If there is some error accessing
    // the file, let the user know.
    // If you don't override this method
    // and an error occurs, an IOException 
    // is thrown.
    @Override
    public FileVisitResult visitFileFailed(Path file,
                                       IOException exc) {
        System.err.println(exc);
        return CONTINUE;
    }
}

class Main {
    public static void main(String[] args) {
        String path = "C:\\Program Files (x86)\\Windows Defender";
        Path startingDir = Paths.get(path);
        PrintFiles pf = new PrintFiles();
        try {
            Files.walkFileTree(startingDir, pf);
        } catch(IOException e) {
            System.err.println("Exception");
        }
    }
}

Ricordati che questo tipo di algoritmi effettuano ricerche in profondità; quindi devi usare l'altro metodo che mostra in quell'esempio, se vuoi limitarlo e non andare troppo oltre.

Con il path che vedi nel mio codice, l'output prodotto nel mio caso è:

Codice:
Regular file: C:\Program Files (x86)\Windows Defender\EppManifest.dll (732984bytes)
Regular file: C:\Program Files (x86)\Windows Defender\it-IT\EppManifest.dll.mui (2560bytes)
Regular file: C:\Program Files (x86)\Windows Defender\it-IT\MpAsDesc.dll.mui (59392bytes)
Directory: C:\Program Files (x86)\Windows Defender\it-IT
Regular file: C:\Program Files (x86)\Windows Defender\MpAsDesc.dll (95048bytes)
Regular file: C:\Program Files (x86)\Windows Defender\MpClient.dll (693984bytes)
Regular file: C:\Program Files (x86)\Windows Defender\MpOAV.dll (221920bytes)
Regular file: C:\Program Files (x86)\Windows Defender\MsMpLics.dll (13024bytes)
Directory: C:\Program Files (x86)\Windows Defender

Mi raccomando, tieni sempre a mente che è importante metterci le mani e provare il codice, fare modifiche etc etc 😉
 
Pubblicità
Pubblicità
Indietro
Top