Set en get

Sonic Pi heeft een globaal memory model genaamd tijd toestand. De twee belangrijkste dingen die je ermee doet is informattie zetten (set) en ophalen (get). Laten we erin duiken…

Set

Om informatie op te slaan in de tijd toestand hebben we twee dingen nodig:

  1. de informatie die we willen opslaan,
  2. een unieke naam (sleutel) voor de informatie.

We willen bijvoorbeeld het getal 3000 opslaan met de sleutel :intensiteit. Dit is mogelijk door de set functie te gebruiken:

set :intensiteit, 3000

We kunnen elke naam gebruiken voor onze sleutel. Wanneer informatie al was opgeslagen voor die sleutel, dan overschrijft de nieuwe set die:

set :intensiteit, 1000
set :intensiteit, 3000

In het bovenstaande voorbeeld hebben we beide getallen opgeslagen onder dezelfde sleutel. De laatste aanroep van set ‘wint’. Het getal dat gekoppeld is aan :intensiteit zal 3000 zijn omdat de eerste aanroep van set effectief wordt overschreven.

Get

Om informatie op te halen uit de tijd toestand hebben we allen de sleuten nodig die werd gebruikt om voor set. In ons geval is dat :intensiteit. We hoeven alleen get[:intensiteit] aan te roepen wat we kunnen zien door het resultaat naar het log af te drukken:

print get[:intensiteit] #=> print 3000

Merk op dat aanroepen van get informatie kunnen teruggeven die was geset in de vorige uitvoering. Wanneer een stuk informatie is geset is het beschikbaar totdat of de informatie is overschreven (net als de aanpassing van :intensiteit van de waarde 1000 naar 3000 hierboven) of Sonic Pi is afgesloten.

Meerdere threads

Het belangrijkste voordeel van het tijd toestand systeem is dat het veilig gebruikt kan worden tussen meerdere threads of live lussen. Je kunt bijvoorbeeld een live lus hebben voor het zetten van informatie en een andere voor het ophalen:

live_loop :opslaan do
  set :foo, rrand(70, 130)
  sleep 1
end
live_loop :ophalen do
  puts get[:foo]
  sleep 0.5
end

Het leuke aan het gebruik van get en set tussen meerdere threads zoals hier is dat het altijd hetzelfde resultaat oplevert iedere keer dat je op uitvoeren drukt. Vooruit, probeer het. Kijk op je het volgende in je log krijgt:

{run: 0, time: 0.0}##run: draai
 └─ 125.72265625
{run: 0, time: 0.5}##run: draai
 └─ 125.72265625
{run: 0, time: 1.0}##run: draai
 └─ 76.26220703125
{run: 0, time: 1.5}##run: draai
 └─ 76.26220703125
{run: 0, time: 2.0}##run: draai
 └─ 114.93408203125
{run: 0, time: 2.5}##run: draai
 └─ 114.93408203125
{run: 0, time: 3.0}##run: draai
 └─ 75.6048583984375
{run: 0, time: 3.5}##run: draai
 └─ 75.6048583984375

Probeer het een paar keer uit te voeren, zie je wel, het is steeds hetzelfde. Dit is wat we deterministisch gerag noemen en het is werkelijk heel belangrijk wanneer we onze muziek als code willen delen en zeker willen weten dat de persoon die code afspeelt exact hetzelfde hoort als wat we wilden dat ze hoorden (net als een MP3 afpselen of een internet stream voor alle luisteraars hezelfde klinkt).

Een simple deterministisch toestandssysteem

Eerder, in sectie 5.6, bespraken we waarom variabelen gebruiken over meerdere threads voor willekeurig gedrag kan zorgen. Daardoor is het betrouwbaar reproduceren zoals de volgende code niet mogelijk:

## Een voorbeeld van niet deterministisch gedrag
## (veroorzaakt door race condities veroorzaakt door meerdere
## live lussen die dezelfde variable manipuleren
## op hetzelfde moment).
##  
## Wanneer je deze code uitvoert zul je opmerken
## dat de lijst die wordt afgedrukt 
## niet altijd gesorteerd is
a = (ring 6, 5, 4, 3, 2, 1)
live_loop :geschud do
  a = a.shuffle
  sleep 0.5
end
live_loop :gesorteerd do
  a = a.sort
  sleep 0.5
  puts "gesorteerd: ", a
end

Laten we kijken hoe dit eruit ziet wanneer we get en set gebruiken:

## Een voorbeeld van deterministisch gedrag
## (ondanks gelijktijdige toegang tot gedeelde toestand)
## door het gebruik van het tijd toestand systeem van Sonic Pi.
##
## Wanneer deze code wordt uitgevoerd is de lijst
## die wordt afgedrukt altijd gesorteerd!
set :a, (ring 6, 5, 4, 3, 2, 1)
live_loop :geschud do
  set :a, get[:a].shuffle
  sleep 0.5
end
live_loop :gesorteerd do
  set :a, get[:a].sort
  sleep 0.5
  puts "gesorteerd: ", get[:a]
end

Merk op dat de code bijna identiek is aan de versie die de variabele gebruikt hiervoor. Echter wanneer je de code uitvoert gedraagt het zich zoals je zou verwachten met elk typisch stuk Sonic Pi code: het doet elke keer hetzelfde in dit geval dankzij het tijd toestand systeem.

Gebruik daarom wanneer je informatie deelt tussen live lussen en threads get en set in plaat van variabelen voor deterministisch en reproduceerbaar gedrag.