Per "divertimento", e per imparare la programmazione funzionale, sto sviluppando un programma in Clojure che fa la composizione algoritmica usando le idee di questa teoria della musica chiamata "Teoria di Westergaardian". Genera linee di musica (dove una linea è solo un singolo rigo costituito da una sequenza di note, ciascuna con altezze e durate). Funziona fondamentalmente in questo modo:Come posso rappresentare una linea di note musicali in un modo che consenta l'inserimento veloce in qualsiasi indice?
- Inizia con una riga composta da tre note (le specifiche di come vengono scelti non sono importanti).
- Esegue in modo casuale una delle diverse "operazioni" su questa linea. L'operazione seleziona casualmente da tutte le coppie di note adiacenti che soddisfano un determinato criterio (per ogni coppia, il criterio dipende solo dalla coppia ed è indipendente dalle altre note nella riga). Inserisce 1 o più note (a seconda dell'operazione) tra la coppia scelta. Ogni operazione ha i suoi criteri univoci.
- Continuare in modo casuale queste operazioni sulla linea fino a quando la linea è la lunghezza desiderata.
Il problema che ho riscontrato è che la mia implementazione di questo è piuttosto lenta, e ho il sospetto che potrebbe essere reso più veloce. Sono nuovo di Clojure e di programmazione funzionale in generale (anche se ho esperienza con OO), quindi spero che qualcuno con più esperienza possa indicare se non sto pensando in un paradigma funzionale o se mi manca una qualche tecnica FP .
La mia attuale implementazione è che ogni linea è un vettore contenente mappe. Ogni mappa ha un: note e a: dur. : il valore della nota è una parola chiave che rappresenta una nota musicale come: A4 o: C# 3. : il valore di dur è una frazione, che rappresenta la durata della nota (1 è una nota intera, 1/4 è una nota da un quarto, ecc ...). Così, per esempio, una linea che rappresenta la C di partenza scala maggiore sulla C3 sarebbe simile a questa:
[
{:note :C3 :dur 1}
{:note :D3 :dur 1}
{:note :E3 :dur 1}
{:note :F3 :dur 1}
{:note :G3 :dur 1}
{:note :A4 :dur 1}
{:note :B4 :dur 1}
]
Questa è una rappresentazione problematica perché non c'è in realtà un modo veloce per inserire in un indice arbitrario di un vettore. Ma l'inserimento è l'operazione eseguita più frequentemente su queste linee. La mia attuale terribile funzione per inserire note in una linea divide il vettore usando subvec nel punto di inserimento, usa conj per unire la prima parte + note + ultima parte, quindi usa flatten e vec per farle tutte in una dimensione unidimensionale vettore. Per esempio, se voglio inserire C3 e D3 nella scala C maggiore all'indice 3 (dove F3 è), lo farebbe (userò il nome della nota al posto delle mappe: note e: dur):
- (conj [C3 D3 E3] [C3 D3] [F3 G3 A4 B4]), che crea [C3 D3 E3 [C3 D3] [F3 G3 A4 B4]]
- (vec (appiattire precedente -vector)) che dà [C3 D3 E3 C3 D3 F3 G3 A4 B4]
Il tempo di esecuzione è O (n), AFAIK.
Sto cercando un modo per rendere questo inserimento più veloce. Ho cercato informazioni sulle strutture dati Clojure che hanno un inserimento veloce ma non hanno trovato nulla che possa funzionare. Ho trovato "finger trees" ma consentono solo l'inserimento rapido all'inizio o alla fine della lista.
Modifica: ho diviso questo in due domande. The other part is here.
Detesto i commenti "+1", ma devo dire: ben fatto nella domanda molto ben fatta. Spiega dettagliatamente il problema e discute le carenze trovate nelle opzioni che hai già esplorato e che altri potrebbero suggerire diversamente. – amalloy
In realtà, si tratta di due domande completamente distinte: una su come inserire nel mezzo di una lista e una su come astrarre strutture di dati. Forse potresti dividerlo in due domande? Alcuni possono avere una risposta a una domanda, ma si sentono male nel dare solo una mezza risposta, e quindi non rispondono a nulla. – amalloy
Ok, lo dividerò. Me lo sono chiesto. Speriamo che una risposta alla seconda domanda non invalidi una risposta al primo. – chairbender