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

9

Mi occupo di riscrivere alcuni vecchi codici VB. Capisco come funziona, ma sento che c'è un modo molto più efficiente di fare ciò che hanno fatto. Non riesco a capire cosa sia. Ecco un esempio forzato che in termini di requisiti dei dati è molto simile a quello che devo fare.

L'utente deve scegliere 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 (make) dovrebbe not avere un'opzione per selezionare Truck perché nessuno dei Subarus è camion. Allo stesso modo, se selezionano Ford, Sedan e Toro, l'ultima casella (colore) dovrebbe non 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 trovato questo (in psuedocode python-y):

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 semplicemente orribile, sia a livello di algoritmo, sia a livello di sintassi. Per uno, analizza l'intero file, quando ha solo bisogno di leggere un paio di righe se fatto bene. Per rendere questo ancora più inefficiente, i miei dati reali hanno 6 opzioni da selezionare, anziché solo 4. Questo è anche l'archiviazione di più dati di cui ha bisogno, data la quantità di duplicazione dei dati.

Sto cercando un modo diverso di archiviare i dati nel file o un modo diverso di analizzarli per rendere la getValidOptions funzionante sia più carina che più efficiente. Ci sono dei modi in cui potrei fare questo?

    
posta DJMcMayhem 16.12.2015 - 02:22
fonte

4 risposte

6

Tutte le altre risposte che ho letto sembrano ignorare due regole di base dello sviluppo del software:

  • chiarisci prima 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 hai scritto troppo di grandi dimensioni, quindi presumo che in realtà non ci sia nulla di sbagliato nelle sue dimensioni eccetto il tuo istinto. Quindi se il caricamento del file 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 nel mantenimento del file, lascia che il formato del file sia così com'è: non sottovalutarlo valore della semplicità.

Ti lamenti anche dell'efficienza dell'algoritmo, che in realtà non è così efficiente come potrebbe essere, ma ha un vantaggio molto grande: è semplice e funziona al cervello. Quindi, purché sia abbastanza efficiente, non applicare alcuna ottimizzazione prematura.

Quindi, supponiamo che il programma funzioni abbastanza velocemente, che cosa dovresti chiedere prima è come puoi migliorare il codice in termini di semplicità e il principio DRY? E questo è davvero un punto che dovrebbe essere migliorato, dal momento che il codice attuale non è SECCO. Nel tuo esempio, compare quattro volte lo stesso test del blocco di codice se le opzioni sui "livelli superiori" corrispondono alla riga corrente, il che risulta in quattro volte lo stesso tipo di chiamata "append" (nel tuo codice reale, la ripetizione avviene almeno sei volte, come hai scritto tu). 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 getValidOptions debba essere chiamato più di una volta (almeno una volta per livello), suggerisco di applicare solo un'ottimizzazione (se non è già il caso): assicurati che la funzione getLine estrae i suoi dati dalla memoria principale, e non legge il file dal disco ancora e ancora.

    
risposta data 16.12.2015 - 08:13
fonte
6

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

Quindi, puoi separare il processo di questi dati in due passaggi:

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

La struttura ad albero può essere implementata con un cosiddetto 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 un altro hash o dizionari in cui ogni chiave è il modello.
  • I loro valori sono i colori. O un'altra struttura che tiene nelle loro chiavi il terzo livello e come valore il quarto livello.
  • E così via ...

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

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

Riferimenti :

1: link
- Una spiegazione completa sull'implementazione di un albero in C #.
- Cerca un commento dove qualcuno chiede di serializzare quell'oggetto e la risposta per quel commento.

    
risposta data 16.12.2015 - 02:48
fonte
3

Un modo semplice per archiviare i dati è 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. Viene evitata la confusione condizionata menzionata nella domanda. Per limitare i dropdown, genera una semplice clausola where su ogni colonna (ad esempio select distinct model where manufacturer='ford' and color = 'red' ).

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

    
risposta data 16.12.2015 - 06:07
fonte
1

Presumo che sia possibile accedere in modo casuale alle righe nel file e quindi trattare il file come una matrice di record. Se non puoi accedere in modo casuale alle linee, allora l'algoritmo che hai è il meglio che puoi fare.

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

Ci sono molti modi per creare indici di file flat. Di solito uso un intervallo di pedici. Mentre l'utente effettua ogni selezione, usa l'intervallo per limitare la lettura del prossimo file.

Ecco come creerei gli indici per i dati di esempio che hai fornito.

Ovviamente il file deve essere ordinato. Ho numerato le linee 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 ciascuna combinazione univoca dei primi tre campi nel file. In ogni record, memorizza 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, usando 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 di esagerare nel concetto, ma in generale crei un indice facendo cadere 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 indice è sempre uno più dell'abbonato "last" del record precedente, o zero se non vi è alcun record precedente. Quindi non è necessario memorizzare i "primi" pedici. Li sto lasciando al loro posto sotto per l'illustrazione.

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

La resa minima della cascata dell'indice termina 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 compila un elenco di selezione da un indice, si utilizzano i "primi" e gli "ultimi" pedici dalla selezione dell'utente nell'indice precedente per limitare le righe lette.

Esempio:

Riempi il primo elenco di selezione tra tutti file0.dat . (Ford, Subaru)

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

Riempi la seconda lista di selezione dalle righe da 0 a 1 di file1.dat . (Camion, Berlina)

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

Come puoi vedere, nel momento in cui l'utente ha selezionato, ad esempio, "Ford" "Sedan" "Taurus", troverai che devi leggere solo le righe dal 6 all'8% di% co_de per riempire la quarta lista di selezione.

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

AGGIUNTO: su ulteriori riflessioni, i file possono essere concatenati in uno solo.

--| 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          -'   -

Questo è usato esattamente come la versione di file multipli, tranne per il fatto che è necessario che la prima linea fittizia contenga il primo intervallo di pedici.

    
risposta data 16.12.2015 - 05:48
fonte

Leggi altre domande sui tag