Sonic PI имеет глобальное хранилище памяти, называемое Временным состоянием. В основном оно используется чтобы “установить” (set) информацию и “получить” (get) информацию. Давайте нырнем глубже…
Чтобы хранить информацию во Временном Состоянии нам нужны две вещи:
Например, мы можем захотеть сохранить число 3000
с ключом :intensity
. Это можно сделать используя функцию set
:
set :intensity, 3000
Мы можем дать ключу любое имя. Если какая-то информация уже хранится под этим именем, наш новый set
перезапишет её:
set :intensity, 1000
set :intensity, 3000
В примере выше, где мы сохранили оба числа под одним ключом, последний вызов set
‘побеждает’, поэтому число связанное с :intensity
будет равно 3000
, так как первый вызов ‘set’ эффективно перезаписан.
Для извлечения информации из Временного Состояния нам просто нужен ключ, который мы использовали в set
, в нашем случае это :intensity
. Тогда нам просто нужно вызвать get[:intensity]
, результат которого выведем на экран:
print get[:intensity] #=> выведет 3000
Обратите внимание, что запросы get
могут возвращать информацию, которая была записана в set
в предыдущем запуске исполнения кода. После того, как часть информации была записана в set
, она становится доступной до тех пор, пока она либо не будет переопределена (точно так же, как мы переопределили значение:intensity
от 1000
к ` 3000`), либо Sonic Pi не будет закрыт.
Основное преимущество системы Time State заключается в том, что ее можно безопасно использовать в потоках или в живых циклах. Например, у вас может быть сделана установка для одного живого цикла, а другой может ее получить:
live_loop :setter do
set :foo, rrand(70, 130)
sleep 1
end
live_loop :getter do
puts get[:foo]
sleep 0.5
end
Хорошая вещь об использовании get
и set
в потоках, подобных этому, заключается в том, что всегда будет выдаваться один и тот же результат всякий раз, когда вы запускаете исполнение кода. Попробуйте этом, и посмотрите, есть ли в вашем журнале следующее:
{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
Попробуйте запустить этот код несколько раз - видите, результат одинаков каждый раз. Это то, что мы называем детерминированным поведением, и это действительно очень важно, когда мы хотим поделиться своей музыкой в виде кода и знать, что человек, играющий код, слышит именно то, что мы хотели, чтобы он услышал (точно так же, как воспроизведение MP3 или интернет-стрим звучат одинаково для всех слушателей).
В разделе 5.6 мы обсуждали, почему использование переменных в потоках может привести к случайному поведению. Это лишает нас возможности надежно воспроизводить код, такой как этот:
## Пример недетерминированного поведения
## (из-за гонки состояний, вызванной несколькими
## живыми циклами, манипулирующими одной и той же переменной
## в одно и то же время).
##
## Если вы запустите этот код, вы заметите
## что список, отображаемый в журнале
## не всегда сортируется!
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
Давайте посмотрим, как это может выглядеть, используя get
и set
:
## Пример детерминированного поведения
## (несмотря на одновременный доступ к общему состоянию)
## используя новую систему Time State от Sonic Pi.
##
## Когда этот код выполняется, список,
## отображаемый в журнале, всегда отсортирован!
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
Обратите внимание, что этот код в значительной степени идентичен версии, использующей переменную. Однако когда вы запускаете код, он ведет себя так, как вы ожидаете с любым типичным кодом Sonic Pi - * он делает то же самое каждый раз * в этом случае благодаря системе Time State.
Поэтому, когда вы делитесь информацией между живыми циклами и потоками, используйте get
и set
вместо переменных для детерминированного, воспроизводимого поведения.