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
The nice thing about using get
and set
across threads like this is that it will always produce the same result every time you hit run. Go on, try it. See if you get the following in your log:
{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
Try running it a few times - see, it’s the same every time. This is what we call deterministic behaviour and it’s really very important when we want to share our music as code and know that the person playing the code is hearing exactly what we wanted them to hear (just like playing an MP3 or internet stream sounds the same for all listeners).
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
Notice how this code is pretty much identical to the version using a variable before it. However when you run the code, it behaves as you would expect with any typical Sonic Pi code - it does the same thing every time in this case thanks to the Time State system.
Therefore, when sharing information across live loops and threads, use get
and set
instead of variables for deterministic, reproducible behaviour.