Sonic Pi tiene un almacén de memoria global llamado Time State. Las dos cosas principales que se hacen con él son set
(establecer) información y get
(obtener) información. Vamos a profundizar…
Para almacenar información en el Time State necesitamos dos cosas:
Por ejemplo, puede ser que queremos almacenar el número 3000
con la clave :intensity
. Eso es posible utilizando la función set
:
set :intensity, 3000
Podemos utilizar cualquier nombre para nuestra clave. Si esta clave ya tiene datos almacenados, nuestro nuevo set
tendrá primacía:
set :intensity, 1000
set :intensity, 3000
En el ejemplo anterior, como almacenamos ambos números bajo la misma clave, la última llamada a set
‘gana’, por lo que el número asociado a :intensidad
será 3000
ya que la primera llamada a set
es efectivamente anulada.
Para obtener la información del Time State sólo necesitamos la clave que utilizamos para set
(establecerlo), que en nuestro caso es :intensity
. Entonces sólo tenemos que llamar a get[:intensity]
que podemos ver imprimiendo el resultado en el registro:
print get[:intensity] #=> imprime 3000
Observa que las llamadas a get
pueden devolver información que fue set
en una ejecución anterior. Una vez que una pieza de información ha sido “fijada”, está disponible hasta que la información sea sobrescrita (al igual que el valor de :intensity
de 1000
a 3000
) o Sonic Pi se cierra.
La principal ventaja del sistema Time State es que puede utilizarse de forma segura entre hilos o bucles vivos. Por ejemplo, puedes tener un bucle vivo estableciendo información y otro obteniéndola:
live_loop :setter do
set :foo, rrand(70, 130)
sleep 1
end
live_loop :getter do
puts get[:foo]
sleep 0.5
end
Lo bueno de usar get
y set
entre hilos de esta manera es que siempre va a producir el mismo resultado cada vez que ejecutas el código. Adelante, intentalo. Verás lo siguiente en tu bitácora:
{run: 0, time: 0.0}
└─ 125.72265625
{run: 0, time: 0.5}
└─ 125.72265625
{run: 0, time: 1.0}
└─ 76.26220703125
{run: 0, time: 1.5}
└─ 76.26220703125
{run: 0, time: 2.0}
└─ 114.93408203125
{run: 0, time: 2.5}
└─ 114.93408203125
{run: 0, time: 3.0}
└─ 75.6048583984375
{run: 0, time: 3.5}
└─ 75.6048583984375
Intenta correrlo unas cuantas veces - ¿Lo ves? Es el mismo resultado cada vez. Esto es lo que llamamos comportamiento determinista y es bastante importante cuando compartimos nuesta música como código y saber que la persona que está ejecutando el código está escuchando exactamente lo que quisimos que escuchara (justo como cuando reproducimos un MP3 o una transmisión por internet, todos los oyentes escuchan lo mismo).
Ya en la Sección 5.6 discutimos por qué el uso de variables entre hilos puede conducir a un comportamiento aleatorio. Esto nos impide poder reproducir de manera fiable un código como este:
## An Example of Non-Deterministic Behaviour
## (due to race conditions caused by multiple
## live loops manipulating the same variable
## at the same time).
##
## If you run this code you'll notice
## that the list that's printed is
## not always sorted!
a = (ring 6, 5, 4, 3, 2, 1)
live_loop :shuffled do
a = a.shuffle
sleep 0.5
end
live_loop :sorted do
a = a.sort
sleep 0.5
puts "sorted: ", a
end
Echemos un vistazo a cómo se vería esto usando get
y set
:
## An Example of Deterministic Behaviour
## (despite concurrent access of shared state)
## using Sonic Pi's new Time State system.
##
## When this code is executed, the list that's
## printed is always sorted!
set :a, (ring 6, 5, 4, 3, 2, 1)
live_loop :shuffled do
set :a, get[:a].shuffle
sleep 0.5
end
live_loop :sorted do
set :a, get[:a].sort
sleep 0.5
puts "sorted: ", get[:a]
end
Nota como este código es casi identico a la versión donde se utiliza una variable antes de. Sin embargo cuando corres el código, se comporta como esperarías de cualquier código típico de Sonic Pi -hace exactamente lo mismo cada vez- en este caso gracias al sistema de Time State.
Siendo así, cuando compartimos información entre live loops e hilos, es preferible usar get
y set
en vez de variables para un comportamiento determinista y reproducible.