10.1 Set und Get

# Set und Get

Sonic Pi verfügt über einen globalen Speicherbereich, der Time-State genannt wird. Die beiden wesentliche damit möglichen Dinge sind das Setzen von Informationen (Set) und das Holen von Information (Get). Lass uns das genauer ansehen ...

## Set

Um Informationen in den Time-State zu speichern benötigen wir zwei Dinge:

1. die Information, die wir speichern wollen,
2. einen eindeutigen Namen (Schlüssel) für die Information.

Zum Beispiel wollen wir vielleicht die Zahl `3000` unter dem Schlüsselnamen `:intensity` speichern. Das ist möglich mit der `set`-Funktion:

```
set :intensity, 3000
```

Wir können für den Schlüssel einen beliebigen Namen verwenden. Wurde unter diesem Schlüsselnamen bereits Information gespeichert, wird unser neuer `set` -Befehl diese überschreiben:

```
set :intensity, 1000
set :intensity, 3000
```

In dem obigen Beispiel haben wir beide Werte unter dem gleichen Schlüsselnamen abgespeichert, und der letzte `set`-Befehl 'gewinnt', so dass die Zahl 3000 mit `:intensity` assoziiert wird, also unser erster `set`-Befehl damit überschrieben wurde.

## Get

Um Informationen aus dem Time-State wiederzuholen, benötigen wir nur den Schlüssel, den wir bei `set` verwendet haben, also in unserem Fall `:intensity`. Wir müssen jetzt nur `get[:intensity]` aufrufen, und das Ergebnis wird uns im Protokoll angezeigt:

```
print get[:intensity] #=> prints 3000
```

Beachte, dass Aufrufe von `get` Informationen zurückgeben können, die bei einem vorherigem Programm-Lauf gesetzt wurden. Wurde eine Information einmal mit Hilfe von `set` gesetzt, bleibt sie solange verfügbar, bis sie entweder überschrieben wird (so wie wir den Wert `1000` von `intensity` mit `3000` vertauscht haben) oder Sonic Pi beendet wird.

## Mehrere Threads

Der wesentliche Vorteil des Time-State-Systems ist, dass es auf sichere Art und Weise zwischen Threads und zwischen Live-Loops verwendet werden kann. Beispielsweise könnte ein Live-Loop Informationen setzen und ein anderer diese auslesen:

```
live_loop :setter do
  set :foo, rrand(70, 130)
  sleep 1
end
live_loop :getter do
  puts get[:foo]
  sleep 0.5
end
```

Das Schöne an der Verwendung von `get` und `set` über Threads hinweg ist, dass immer das gleiche Ergebnis ausgegeben wird, wenn der Programm-Code gestartet wird. Los, probiere es selbst. Sieh nach, ob dir das Folgende im Protokoll ausgegeben wird:

```
{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
```

Starte das Programm einfach mehrmals hintereinander - du siehst, dass das Ergebnis jedes Mal das Gleiche ist. Wir bezeichnen dies als deterministisches (vorhersagbares) Verhalten, und es ist sehr wichtig, wenn wir unsere Musik als Programm-Code weitergeben, zu wissen, dass die Person, die den Code ausführt, genau das hören wird, was wir sie hören lassen wollten (ähnlich wie beim Abspiel einer MP3 oder beim Streamen für alle Hörer dasselbe zu hören ist).

## Ein einfaches deterministisches State-System

In Abschnitt 5.6 haben wir darüber gesprochen, wie die Nutzung von Variablen über verschiedenen Threads hinweg zu zufälligen Resultaten führen kann. Das hindert uns daran Programm-Code wie diesen zuverlässig zu reproduzieren:

```
## Ein Beispiel für ein nicht-deterministisches Verhalten
## (aufgrund von Race Conditions, die durch
## mehrfache Live-Loops ausgelöst werden, die den Wert
## der selben Variable verändern).
##  
## Wenn du den Code startest, siehst du,
## dass die Liste, die ausgegeben wird,
## nicht immer sortiert ist!
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
```

Lass uns sehen, wie wir hier `get` und `set` einsetzen könnten:

```
## Ein Beispiel für deterministisches Verhalten
## (trotz gleichzeitigem Zugriff auf einen geteilten Zustand)
## mit Hilfe des Sonic Pi Time State Systems.
##
## Bei Ausführung dieses Codes,
## wird die Liste immer sortiert ausgegeben!
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
```

Beachte, dass dieser Code fast identisch ist mit dem Code, der eine Variable zum Teilen der Information verwendet hat. Wenn du ihn ausführst verhält er sich allerdings so, wie du es bei jedem typischen Sonic Pi Code erwarten würdest - *er verhält sich immer gleich*, in diesem Fall dank des Time-State-Systems.

Deshalb verwende für ein vorhersagbares und reproduzierbares Verhalten anstelle von Variablen immer `get` und `set`, wenn du Daten zwischen Live-Loops und zwischen Threads teilst.
