Set и Get

Sonic PI имеет глобальное хранилище памяти, называемое Временным состоянием. В основном оно используется чтобы “установить” (set) информацию и “получить” (get) информацию. Давайте нырнем глубже…

Set

Чтобы хранить информацию во Временном Состоянии нам нужны две вещи:

  1. информация, которую мы хотим сохранить,
  2. уникальное имя (ключ) для этой информации.

Например, мы можем захотеть сохранить число 3000 с ключом :intensity. Это можно сделать используя функцию set:

set :intensity, 3000

Мы можем дать ключу любое имя. Если какая-то информация уже хранится под этим именем, наш новый set перезапишет её:

set :intensity, 1000
set :intensity, 3000

В примере выше, где мы сохранили оба числа под одним ключом, последний вызов set ‘побеждает’, поэтому число связанное с :intensity будет равно 3000, так как первый вызов ‘set’ эффективно перезаписан.

Get

Для извлечения информации из Временного Состояния нам просто нужен ключ, который мы использовали в 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 вместо переменных для детерминированного, воспроизводимого поведения.