# 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.