Qual è il modo più veloce per trasformare in caratteri gli elementi di un elenco di elenchi di numeri? Questa sfida è un'estensione della questione che si trova qui nella Comunità. Ho deciso di testare otto approcci: tre metodi iterativi ( For , For Each e Transform Each ), quattro metodi non iterativi (operazioni su tabelle, espressioni, Words + espressioni e Regex ) e un ibrido ( Words + Transform Each ). Cinque di questi metodi, quelli che non coinvolgono Regex o espressioni, sono stati considerati nel post collegato. I metodi che utilizzano Words e Regex sono un po' un trucco in quanto sfruttano un effetto collaterale delle operazioni sulle stringhe: in particolare, qualsiasi struttura di dati può essere trasformata in una stringa di caratteri e analizzata di conseguenza. Lascerò decidere al lettore se ritiene legittimo questo approccio. Conosco alcune persone che disapproverebbero la cosa!
Utilizzando un design che riempie lo spazio, ho esaminato quattro fattori: numero di elenchi, numero di elementi dell'elenco, se gli elenchi avevano un numero uguale o diverso di elementi e tipo di numero: intero o float. Il progetto si trova in Elenco di elenchi – Esecuzioni sperimentali.JMP . I valori frazionari della generazione del progetto originale sono stati arrotondati. Sono state effettuate dieci repliche per ciascuna corsa. Il codice che ho utilizzato per eseguire l'esperimento si trova in Esegui elenco di elenchi sperimentale.JSL e i risultati in Elenco di elenchi risultati.JMP. Le esecuzioni preliminari con Regex erano estremamente lente e non furono prese in considerazione.
Per confrontare i metodi, calcolo l'ordine di classificazione di ciascun metodo per esecuzione e replica e la differenza tra il tempo di esecuzione e il tempo di esecuzione mediano per tutte le repliche all'interno di un'esecuzione sperimentale. Più piccolo è meglio. I grafici seguenti suggeriscono che Transform Each , le operazioni sulla tabella e i metodi Words erano superiori. Sebbene la differenza temporale effettiva sia piccola (i valori sono espressi in microsecondi), questi metodi sono costantemente classificati più in alto e presentano una minore variabilità.


Per vedere se c'erano differenze tra i quattro metodi, ho eseguito nuovamente l'esperimento con elenchi più grandi ( Elenco di elenchi grandi – Esecuzioni sperimentali.JMP e Elenco di elenchi grandi risultati.JMP ). Osservando le differenze di tempo rispetto al tempo mediano all'interno dell'esecuzione, sembra che all'aumentare del numero totale di elementi, le operazioni Trasforma ogni e quelle di tabella ottengono risultati migliori. Questo è un risultato fortunato, poiché Trasforma ciascuno è stato appena introdotto in JMP 16 e le operazioni sulle tabelle forniscono una buona soluzione per coloro che utilizzano ancora JMP 15.

Per la seconda sfida, ti ho chiesto di trasporre un elenco di elenchi. Un elenco di elenchi trasposto raccoglie tutti i primi elementi dagli elenchi di input nel primo elenco di output, i secondi elementi di input nel secondo elenco di output, ecc. Se l'input avesse n elenchi, ogni elenco di output avrebbe n elementi. Il numero di elenchi di output equivarrebbe al numero massimo di elementi in ciascun elenco. Mentre il numero di elenchi di input è facile da determinare, il numero di elenchi di output richiede il conteggio degli elementi in ciascun elenco di input. È necessario? Porta ad un processo più veloce?
Per rispondere a questa domanda e testare metodi specifici, ho eseguito un altro progetto di riempimento dello spazio. In esso, ho esaminato tre metodi iterativi ( For , For Each e Transform Each ), ciascuno utilizzando due approcci: uno che calcola il numero di elenchi di output prima di creare l'output, richiedendo il ciclo sull'input due volte, e l'altro che crea l'output utilizzando un singolo loop sull'input. Inoltre, ho esaminato l'utilizzo delle operazioni sulle tabelle. Sono stati considerati sei diversi tipi di elementi di elenco (intero, float, 1 carattere, 8 caratteri, 15 caratteri e carattere a larghezza variabile (lunghezza massima 15) nonché lunghezze di elenco fisse e variabili (interne).
Durante la mia codifica iniziale, ho scoperto che è più veloce creare l'elenco di output iniziando con un elenco vuoto e utilizzando Inserisci in piuttosto che precompilare l'output con valori mancanti utilizzando Ripeti o espressioni. Puoi utilizzare il codice in Prebuild o Insert.JSL per verificarlo tu stesso. Una seconda scoperta ha riguardato Transform Each e For Each . Se non hai familiarità con queste funzioni, si comportano in modo leggermente diverso rispetto al ciclo For o While standard in quanto ti consentono di scorrere elenchi, matrici o array associativi, senza dover specificare la loro dimensione. Ciò può rendere il codice un po' più compatto e più pulito. La struttura di una chiamata di base a una di queste funzioni è
fnName ({ indValue , ind }, contenitore , corpo )
dove fnName è For Each o Transform Each , indValue è il valore del contenitore nell'indice ind , container è una matrice, un elenco o un array associativo e body è il corpo del codice. È necessario prestare attenzione quando si lavora con contenitori di grandi dimensioni. L'utilizzo di strutture dati di grandi dimensioni come contenitore o indValue rallenterà notevolmente l'esecuzione. Un esempio di ciò è fornito in Using For Each e Transform Each.JSL . In esso, contiamo il numero di elementi in ciascun elenco di un elenco di elenchi. I primi due blocchi di codice corrispondono a come ci si potrebbe aspettare di codificare il problema, il primo utilizzando ciascuna sottolista come valore di input, il secondo indicizzando le sottoliste. Confrontalo con il terzo blocco in cui gli indici di aList vengono creati e utilizzati esplicitamente nel ciclo. A seconda delle dimensioni di aList , i miglioramenti alla velocità di elaborazione possono essere 100X o superiori. Ancora più veloce è Transform Each , che gli consente di restituire i valori risultanti. I blocchi 4 e 5 mostrano che la struttura dei dati di output sarà la stessa della struttura dei dati utilizzata per il contenitore di input. Esiste un argomento Output() facoltativo per controllare il tipo di struttura dei dati di output.
Il codice per testare i diversi metodi di trasposizione si trova in Transpose list.JSL . Come sopra, è stata utilizzata una progettazione a riempimento di spazio per confrontare i risultati in condizioni diverse. A differenza del codice precedente, per ogni ripetizione viene creata una nuova struttura dati. I primi tre metodi leggono l'input e costruiscono l'output in un unico blocco. I tre successivi predeterminano innanzitutto la dimensione degli elenchi di output, prima di elaborare l'input. Il penultimo metodo utilizzava operazioni di tabella e l'ultimo Trasforma ciascuno, strutturato per utilizzare i relativi valori di output.

Predeterminare la dimensione degli elenchi di output si è rivelato più veloce rispetto al tentativo di farlo in un singolo blocco. Le diverse implementazioni di Transform Each mostrano i miglioramenti di velocità ottenuti lavorando con i suoi valori di output. I risultati si trovano in Transpose results.JMP . Un esperimento con elenchi più grandi che confrontano For Each , Transform Each ( 2a implementazione) e le operazioni sulle tabelle è disponibile in Transpose results big.JMP . I risultati suggeriscono che le operazioni sulle tabelle, in generale, superano gli altri due approcci.

Per ricapitolare ciò che abbiamo scoperto:
- Nel tempo: Trasforma Ciascuno < Per Ciascuno < Per
- Le operazioni sulle tabelle possono essere estremamente veloci se non sono coinvolte le formule di colonna. Nei post precedenti abbiamo visto che, a seconda della loro complessità, le formule di colonna possono rallentare le operazioni fino al punto in cui l'iterazione o le espressioni sono favorevoli.
Non ho ricevuto il feedback tipico per questa sfida che ho avuto in passato, quindi sono sicuro che ci sia un approccio migliore. Mi piacerebbe sentire i tuoi commenti. Cerca una nuova sfida il mese prossimo.