Qual è il modo più efficiente per archiviare questi dati?


9

Mi occupo di riscrivere un vecchio codice VB. Capisco come funziona, ma sento che esiste un modo molto più efficiente per fare ciò che hanno fatto. Non riesco proprio a capire di cosa si tratta. Ecco un esempio inventato che in termini di requisiti dei dati è davvero simile a quello che devo fare.

L'utente deve selezionare il produttore, la marca, il modello e il colore della propria auto in una GUI. Ho un grande file di testo che assomiglia a questo:

Ford Truck F150 red
Ford Truck F150 blue
Ford Truck F150 black
Ford Truck F150 silver
Ford Truck F250 red
Ford Truck F250 green
Ford Sedan Taurus red
Ford Sedan Taurus green
Ford Sedan Taurus white
Ford...
...

Subaru SUV Forester blue
Subaru SUV Forester red
Subaru SUV Outback Black
Subaru SUV Outback Green
Subaru SUV Outback Blue
Subaru SUV Outback Red
Subaru...
...

etc.

Quindi, se la prima selezione è Subaru, la seconda casella (marca) non dovrebbe avere un'opzione per selezionare Camion perché nessuno dei Subarus è camion. Allo stesso modo, se selezionano Ford, Berlina e Taurus, l'ultima casella (colore) non dovrebbe mostrare un'opzione per selezionare il blu. O nero. O qualsiasi cosa diversa da rosso, verde o bianco.

Le persone che hanno scritto il codice prima di me hanno escogitato questo (in python-y psuedocode):

def getValidOptions():
    items = []
    for i from 0 to numRows:
        options = getLine().split()
        if selectingManufacturer:
            if options[0] not in items:
                items.append(options[0])
        else if selectingMake:
            if selectedManufacturer == options[0] and options[1] not in items:
               items.append(options[1])
        else if selectingModel:
            if selectedManufacturer == options[0] and selectedMake == options[1] and options[2] not in items:
                items.append(options[2])
        else if selectingColor:
            if selectedManufacturer == options[0] and selectedMake == options[1] and selectedModel == options[2] and options[3] not in items:
                items.append(options[3])
    return items

Penso che sia solo orribile, sia a livello di algoritmo, sia a livello di sintassi. Per uno, analizza l'intero file, quando deve solo leggere un paio di righe se fatto bene. Per renderlo ancora più inefficiente, i miei dati reali hanno 6 opzioni tra cui scegliere, piuttosto che solo 4. Questo sta anche memorizzando più dati di quanti ne abbia bisogno, data la quantità di duplicazione dei dati.

Sto cercando un modo diverso di archiviare i dati nel file o un modo diverso di analizzarlo per rendere la getValidOptionsfunzione sia più carina che più efficiente. Ci sono modi in cui potrei farlo?


2
perché non usare un database?
Tulains Córdova,

Risposte:


6

Tutte le altre risposte che leggo sembrano ignorare due regole basilari di sviluppo del software:

  • chiarire innanzitutto i requisiti (in particolare i requisiti di prestazioni e archiviazione)

  • Keep It Simple, Stupid (vedi KISS )

Hai scritto "il file di testo è grande", ma non ha scritto troppo grande, quindi suppongo che in realtà non ci sia nulla di sbagliato nelle sue dimensioni tranne il tuo istinto. Quindi, se il caricamento del file in realtà non richiede troppo tempo e il tuo reparto IT o qualcun altro non si lamenta dello spazio su disco sprecato e nessuno si lamenta di avere problemi a mantenere il file, lascia che il formato del file sia così - non sottovalutare il valore della semplicità.

Ti stai anche lamentando dell'efficienza dell'algoritmo, che in realtà non è così efficiente come potrebbe essere, ma ha un grande vantaggio: è un cervello morto semplice e funziona. Quindi, fintanto che è abbastanza efficiente, non applicare alcuna ottimizzazione prematura.

Quindi supponiamo che il programma funzioni abbastanza velocemente, quello che dovresti chiedere prima è come puoi migliorare il codice in termini di semplicità e principio DRY? E questo è davvero un punto che dovrebbe essere migliorato, poiché il codice attuale non è DRY. Nel tuo esempio, appare quattro volte lo stesso test del blocco di codice se le opzioni sui "livelli superiori" corrispondono alla riga corrente, il che si traduce in quattro volte lo stesso tipo di chiamata "append" (nel tuo codice reale, si verifica la ripetizione almeno sei volte, come hai scritto). Puoi facilmente evitarlo introducendo un livello di selezione numerico o passando le opzioni già selezionate in un elenco ordinato. Usando il tuo pseudo-codice, questo porta a qualcosa sulla falsariga di

# selectedOptions is a list, containing either nothing, or "selectedManufacturer"
# or [selectedManufacturer, selectedMake], ..., and so on
def getValidOptions(selectedOptions):
    items = []
    level = selectedOptions.size()
    for i from 0 to numRows:
        options = getLine().split()
        if selectedOptions == options[0:level-1] and options[level] not in item:
            items.append(options[level])
    return items

Quindi questo è essenzialmente lo stesso algoritmo senza più codice ripetuto.

Poiché è ovvio che getValidOptionsdeve essere chiamato più di una volta (almeno una volta per livello), suggerisco di applicare solo un'ottimizzazione (se non è già il caso): assicurarsi che la getLinefunzione recuperi i suoi dati dalla memoria principale e non leggere il file dal disco ancora e ancora.


Si desidera spostare "level = selectedOptions.size ()" prima del ciclo numRows.
AI Breveleri,

6

Bene, i dati che hai hanno una struttura ad albero, in cui per ogni produttore hai un albero di modelli e per ogni modello hai un colore (e così via).

Quindi, potresti separare il processo di questi dati in due modi:

  1. Dopo qualsiasi aggiornamento al file di testo, è necessario elaborare quel file e convertirlo in una struttura ad albero.
  2. Quando si carica l'applicazione, si carica solo la struttura ad albero.

La struttura ad albero può essere implementata con quello che viene chiamato hash , un array associativo o un dizionario in lingue come Java, PHP, Javascript o Python. Con questa struttura, hai:

  • Le prime chiavi sono i produttori.
  • I loro valori sono solo altri hash o dizionari in cui ogni chiave è il modello.
  • I loro valori sono i colori. O un'altra struttura mantenendo nelle loro chiavi il terzo livello e come valore il quarto livello.
  • E così via...

A seconda del linguaggio di programmazione, questo può essere implementato più velocemente o più lentamente. Per esempio:

  • C # : è possibile implementare una struttura ad albero 1 e quindi contrassegnarla come serializzabile.
  • VB.Net : è possibile generare la struttura ad albero in un'applicazione e serializzarla in un file.
    • Per questo, qualcosa del genere Runtime.Serialization.Formatters.Binary.BinaryFormatterpotrebbe essere utile, ma non sono un esperto nella serializzazione con VB.Net.
  • Javascript : è possibile generare la struttura ad albero in un file JSON, che deve essere caricato ogni volta che viene caricata l'app.
  • PHP : è possibile generare una versione serializzata della struttura dei dati dell'albero o caricare anche un JSON.
  • Java : puoi serializzare quella struttura di dati, creando un Classche implementa l'interfaccia java.io.Serializable.

Riferimenti :

1: https://dvanderboom.wordpress.com/2008/03/15/treet-implementing-a-non-binary-tree-in-c/
- Una spiegazione completa sull'implementazione di un albero in C #.
- Cerca un commento in cui qualcuno ti chiede di serializzare quell'oggetto e la risposta per quel commento.


1
Sì, avevo pensato di usare un albero, ma non so se sia l'idea migliore perché non esiste una struttura ad albero incorporata (che conosco) in C #, e il progetto è piuttosto piccolo, quindi non so se varrebbe la pena dedicare molto tempo a lavorare su tree with an arbitrary number of nodesun'implementazione.
James,

Fino ad ora non sono esperto di C #, ma almeno in altre lingue come Java e PHP puoi avere una sorta di dizionari, dove ogni chiave può avere come valore un altro dizionario.
Nicolás,

Ho aggiornato la mia risposta. Scopri cosa ne pensi dell'alternativa a hash o dizionario. Ho anche aggiunto un riferimento con un articolo interessante.
Nicolás,

3

Un modo semplice per archiviare i dati è semplicemente inserirli in un database SQLite. SQLite, a differenza della maggior parte dei database SQL, è adatto all'uso come formato di file dell'applicazione. Questo approccio ha diversi vantaggi:

  1. Non è necessario codificare un serializzatore o un deserializzatore.
  2. Il file è modificabile e interrogabile da numerosi programmi esistenti.
  3. Il disordine condizionale che citi nella domanda è evitato. Per limitare i menu a discesa, genera una clausola where semplice per ogni colonna (ad esempio, select distinct model where manufacturer='ford' and color = 'red').

Questo ti costringe a imparare l'SQL, ma evita la necessità di imparare un formato di file personalizzato.


1

Presumo che tu possa accedere casualmente alle linee nel file e quindi trattare il file come un array di record. Se non riesci ad accedere casualmente alle linee, l'algoritmo che hai è il meglio che puoi fare.

Per un accesso più rapido, archiviare i dati in 6 file, in cui ogni file è un indice nel successivo.

Ci sono molti modi per creare indici flatfile. Di solito uso una gamma di sottoscrizioni. Man mano che l'utente effettua ciascuna selezione, utilizzare l'intervallo per limitare la lettura del file successivo.

Ecco come vorrei creare gli indici per i dati di esempio forniti.

Ovviamente il file deve essere ordinato. Ho numerato le righe per l'illustrazione; i numeri di riga non dovrebbero apparire nel file.

--| file3.dat |--
 0 Ford Truck F150 red
 1 Ford Truck F150 blue
 2 Ford Truck F150 black
 3 Ford Truck F150 silver
 4 Ford Truck F250 red
 5 Ford Truck F250 green
 6 Ford Sedan Taurus red
 7 Ford Sedan Taurus green
 8 Ford Sedan Taurus white
 9 Subaru SUV Forester blue
10 Subaru SUV Forester red
11 Subaru SUV Outback Black
12 Subaru SUV Outback Green
13 Subaru SUV Outback Blue
14 Subaru SUV Outback Red

Per creare il primo indice, creare un record per ogni combinazione univoca dei primi tre campi nel file. In ogni record, archivia il primo e l'ultimo numero di riga in cui appare quella combinazione.

--| file2.dat |--
 0 Ford Truck F150       0   3
 1 Ford Truck F250       4   5
 2 Ford Sedan Taurus     6   8
 3 Subaru SUV Forester   9  10
 4 Subaru SUV Outback   11  14

Il secondo indice è costruito in modo simile, utilizzando i primi due campi del primo indice.

--| file1.dat |--
 0 Ford Truck        0   1
 1 Ford Sedan        2   2
 2 Subaru SUV        3   4

E il terzo, il livello più alto in questo caso, indice.

--| file0.dat |--
 0 Ford          0   1
 1 Subaru        2   2

Penso che sto spiegando troppo il concetto, ma in generale crei un indice rilasciando l'ultimo campo ed eliminando i record duplicati.

È possibile ridurre ulteriormente i requisiti di archiviazione dei file eliminando alcuni dati ridondanti.

Ad esempio, il "primo" pedice in ogni record dell'indice è sempre uno in più rispetto al "ultimo" pedice del record precedente o zero se non esiste un record precedente. Quindi non è necessario memorizzare i "primi" indici. Li lascerò sul posto qui sotto per l'illustrazione.

Inoltre, poiché utilizzerai solo l'ultimo campo in ciascun record per riempire l'elenco di selezione, non è necessario memorizzare gli altri campi.

Il rendering minimo dell'indice cascade finisce per apparire così, dove il segno di spunta 'indica un numero non effettivamente memorizzato nel file.

--| file0.dat |--
 0' Ford         0'   1
 1' Subaru       2'   2

--| file1.dat |--
 0' Truck        0'   1
 1' Sedan        2'   2
 2' SUV          3'   4

--| file2.dat |--
 0' F150         0'   3
 1' F250         4'   5
 2' Taurus       6'   8
 3' Forester     9'  10
 4' Outback     11'  14

--| file3.dat |--
 0' red
 1' blue
 2' black
 3' silver
 4' red
 5' green
 6' red
 7' green
 8' white
 9' blue
10' red
11' Black
12' Green
13' Blue
14' Red

Quando si riempie un elenco di selezione da un indice, si utilizzano i "primi" e gli "ultimi" indici della selezione dell'utente nell'indice precedente per limitare le righe lette.

Esempio:

Compili il primo elenco di selezione da tutti file0.dat. (Ford, Subaru)

L'utente seleziona "Ford". Gli abbonamenti corrispondenti sono 0 e 1.

Riempi il secondo elenco di selezione dalle righe da 0 a 1 di file1.dat. (Camion, Berlina)

L'utente seleziona "Berlina". Gli abbonamenti corrispondenti sono 2 e 2.

Come puoi vedere, quando l'utente ha selezionato, ad esempio "Ford" "Berlina" "Taurus", troverai che devi solo leggere le righe da 6 a 8 di file3.datper riempire il quarto elenco di selezione.

Mi scuso per la descrizione piuttosto lunga, ma qui è molto tardi e non ho tempo di scriverne una breve.

AGGIUNTO: A ulteriore considerazione, i file possono essere concatenati in uno.

--| file.dat |--
 0' -            1'   2
 1' Ford         3'   4
 2' Subaru       5'   5
 3' Truck        6'   7
 4' Sedan        8'   8
 5' SUV          9'  10
 6' F150        11'  14
 7' F250        15'  16
 8' Taurus      17'  19
 9' Forester    20'  21
10' Outback     22'  25
11' red          -'   -
12' blue         -'   -
13' black        -'   -
14' silver       -'   -
15' red          -'   -
16' green        -'   -
17' red          -'   -
18' green        -'   -
19' white        -'   -
20' blue         -'   -
21' red          -'   -
22' Black        -'   -
23' Green        -'   -
24' Blue         -'   -
25' Red          -'   -

Viene utilizzato esattamente come la versione di più file, tranne per il fatto che è necessaria la prima riga fittizia per contenere il primo intervallo di pedici.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.