[ < ] [ > ]   [ << ] [ Nach oben ] [ >> ]         [Anfang] [Inhalt] [Index] [ ? ]

1.1 Einleitung in Scheme

@translationof Introduction to Scheme

Wir wollen mit einer Einführung in Scheme beginnen. Für diese kurze Einfürung soll der GUILE-Interpreter genommen werden, um zu erforschen, wie die Sprache funktioniert. Nach besserer Bekanntschaft mit Scheme soll gezeigt werden, wie die Sprache in LilyPond-Dateien eingefügt werden kann.


[ < ] [ > ]   [ << ] [ Nach oben ] [ >> ]         [Anfang] [Inhalt] [Index] [ ? ]

1.1.1 Scheme-Sandkasten

@translationof Scheme sandbox

Die LilyPond-Installation enthält gleichzeitig auch die Guile-Implementation von Scheme. Auf den meisten Systemen kann man in einer Scheme-sandbox experimentieren, indem man ein Kommandozeilen-Fenster öffnet und guile aufruft. Unter einigen Systemen, insbesondere unter Windows, muss man evtl. die Umgebungsvariable GUILE_LOAD_PATH auf das Verzeichnis ../usr/share/guile/1.8 innerhalb des LilyPond-Installationsverzeichnisses setzen (der vollständige Pfad ist erklärt in (learning)Mehr Information in Abschnitt ‘Mehr Information’ in Handbuch zum Lernen). Alternativ können Windows-Benutzer auch einfach „Ausführen“ im Startmenü wählen und guile schreiben.

Es gibt auch eine direkte Scheme-Umgebung mit allen LilyPond-Voreinstellungen, die man auf der Kommandozeile mit folgendem Befehl aufrufen kann:

 
lilypond scheme-sandbox

Wenn guile einmal läuft, erhält man die Eingabeaufforderung von guile:

 
guile>

Man kann Scheme-Ausdrucke hier eingeben und mit Scheme experimentieren. Siehe die Datei ‘ly/scheme-sandbox.ly’ zu Information, wie man die GNU readline-Bibliothek benutzen kann, um bessere Scheme-Formatierung der Kommandozeile zu erhalten. Wenn die readline-Bibliothek für interaktive Guile-Sitzungen außerhalb von LilyPond schon aktiviert ist, sollte es auch in der Sandbox funktionieren.


[ < ] [ > ]   [ << ] [ Nach oben ] [ >> ]         [Anfang] [Inhalt] [Index] [ ? ]

1.1.2 Scheme-Variablen

@translationof Scheme variables

Scheme-Variablen können jedlichen gültigen Scheme-Wert erhalten, auch Scheme-Prozeduren.

Scheme-Variablen werden mit define definiert:

 
guile> (define a 2)
guile>

Scheme-Variablen können an der Guile-Eingabeaufforderung ausgewertet werden, indem man einfach die Variable eintippt.

 
guile> a
2
guile>

Scheme-Variablen können auf dem Bildschirm ausgegeben werden, indem man display zum Anzeigen benutzt:

 
guile> (display a)
2guile>

Sowohl der Wert 2 als auch die Eingabeaufforderung guile werden auf der gleichen Zeile ausgegeben. Das kann man vermeiden, indem man eine newline-Prozedur für eine Leerzeile aufruft oder das Zeichen für eine neue Zeile anzeigen lässt:

 
guile> (display a)(newline)
2
guile> (display a)(display "\n")
2
guile>

Wenn eine Variable einmal erstellt wurde, kann ihr Wert durch set! verändert werden:

 
guile> (set! a 12345)
guile> a
12345
guile>

[ < ] [ > ]   [ << ] [ Nach oben ] [ >> ]         [Anfang] [Inhalt] [Index] [ ? ]

1.1.3 Einfache Scheme-Datentypen

@translationof Scheme simple data types

Das Grundlegendste an einer Sprache sind Daten: Zahlen, Zeichen, Zeichenketten, Listen usw. Hier ist eine Liste der Datentypen, die für LilyPond-Eingabedateien relevant sind.

Boolesche Variablen

Werte einer Booleschen Variable sind Wahr oder Falsch. Die Scheme-Entsprechung für Wahr ist #t und für Falsch #f.

Zahlen

Zahlen werden wie üblich eingegeben, 1 ist die (ganze) Zahl Eins, während -1.5 eine Gleitkommazahl (also eine nicht-ganze) ist.

Zeichenketten

Zeichenketten werden in doppelte Anführungszeichen gesetzt:

 
"Das ist eine Zeichenkette"

Zeichenketten können über mehrere Zeilen reichen:

 
"Das
ist
eine Zeichenkette"

und die Zeichen für eine neue Zeile am Ende jeder Zeile werden auch in die Zeichenkette aufgenommen.

Zeichen für eine neue Zeile können auch hinzugefügt werden, indem man \n in die Zeichenkette aufnimmt.

 
"das\nist eine\nmehrzeilige Zeichenkette"

Anführungszeichen und neue Zeilen können auch mit sogenannten Fluchtsequenzen eingefügt werden. Die Zeichenkette a sagt "b" wird wie folgt eingegeben:

 
"a sagt \"b\""

Weitere zusätzliche Scheme-Datentypen, die hier nicht besprochen wurden, finden sich in einer vollständigen Liste der Scheme-Datentypen in der Guile-Referenzanleitung: http://www.gnu.org/software/guile/manual/html_node/Simple-Data-Types.html.


[ < ] [ > ]   [ << ] [ Nach oben ] [ >> ]         [Anfang] [Inhalt] [Index] [ ? ]

1.1.4 Zusammengesetzte Scheme-Datentypen

@translationof Scheme compound data types

Es gibt auch zusammengesetzte Datentypen in Scheme. Die Datentypen, die in LilyPond häufig benutzt werden, beinhalten Paare, Listen, assoziative Listen und Hash-Tabellen.

Paare (pair)

Der wichtigeste zusammengesetzte Datentyp in Scheme ist ein Paar (pair). Wie aus dem Namen schon hervorgeht, besteht ein Paar aus zwei zusammengeschweißten Werten. Die Prozedur um ein Paar zu erstellen ist cons.

 
guile> (cons 4 5)
(4 . 5)
guile>

Das Paar wird dargestellt als zwei Elemente, von Klammern umgeben und durch Leerzeichen und einen Punkt (.) getrennt. Der Punkt ist kein Dezimalpunkt, sondern markiert die Gruppe als Paar.

Paare können auch wörtlich eingegeben werden, indem man ihnen voraus ein einfaches Anführungszeichen (' setzt.

 
guile> '(4 . 5)
(4 . 5)
guile>

Beide Elemente eines Paares können beliebige gültige Scheme-Werte sein:

 
guile> (cons #t #f)
(#t . #f)
guile> '("blah-blah" . 3.1415926535)
("blah-blah" . 3.1415926535)
guile>

Das erste Element eines Paares kann mit der Prozedur car, das zweite mit der Prozedur cdr angesprochen werden.

 
guile> (define mypair (cons 123 "hello there")
... )
guile> (car mypair)
123
guile> (cdr mypair)
"hello there"
guile>

Achtung: cdr wird ausgeprochen wie "kudd-err", nach Sussman und Abelson, siehe http://mitpress.mit.edu/sicp/full-text/book/book-Z-H-14.html#footnote_Temp_133

Listen (list)

Ein sehr häufiger Datentyp in Scheme ist die Liste (list). Formal gesehen wird eine Liste entweder als leere Liste definiert (repräsentiert als '()), oder als ein Paar, dessen cdr eine Liste ist.

Es gibt viele Arten, Listen zu erstellen. Die vielleicht häufigste Methode ist die list-Prozedur:

 
guile> (list 1 2 3 "abc" 17.5)
(1 2 3 "abc" 17.5)

Wie man sehen kann, wird eine Liste dargestellt in Form der einzelnen Elemente, getrennt durch ein Leerzeichen und als Gruppe in Klammern eingeschlossen. Anders als bei einem Paar, befindet sich kein Punkt zwischen den Elementen.

Eine Liste kann auch als wörtliche Liste notiert werden, indem man die enthaltenen Elemente in Klammern einschließt und ein einfaches Anführungszeichen voranschreibt:

 
guile> '(17 23 "foo" "bar" "bazzle")
(17 23 "foo" "bar" "bazzle")

Listen haben eine zentrale Stellung in Scheme. Scheme wird als Dialekt von Lisp angesehen, und das Wort „lisp“ steht für „List Processing“. Scheme-Ausdrücke sind immer Listen.

Assoziative Listen (alist)

Eine besonderer Listentyp ist die assoziative Liste oder alist. Eine Alist wird benutzt, um Daten zum einfachen Abrufen zu speichern.

Alisten sind Listen, deren Elemente als Paare kommen. Der car-Teil jedes Elements wird als Schlüssel (key) bezeichnet, der cdr-Teil jedes Elements wird Wert (value) genannt. Die Scheme-Prozedur assoc wird benutzt, um einen Eintrag aus einer Aliste aufzurufen, und mit cdr wird sein Wert abgefragt:

 
guile> (define my-alist '((1  . "A") (2 . "B") (3 . "C")))
guile> my-alist
((1 . "A") (2 . "B") (3 . "C"))
guile> (assoc 2 my-alist)
(2 . "B")
guile> (cdr (assoc 2 my-alist))
"B"
guile>

Alisten werden sehr viel in LilyPond genutzt, um Eigenschaften und andere Daten zu speichern.

Hash-Tabellen (hash table)

Eine Datenstruktur, die ab und zu in LilyPond eingesetzt wird. Eine Hash-Tabelle ähnelt einem Array, aber die Indexe des Arrays können beliebige Scheme-Werte sein, nicht nur Integre.

Hash-Tabellen sind effizienter als Alisten, wenn man viele Daten speichern will und die Daten sich oft ändern.

Die Syntax, mit der Hash-Tabellen erstellt werden, ist etwas komplex, aber man kann Beispiele hierzu im LilyPond-Quellcode finden.

 
guile> (define h (make-hash-table 10))
guile> h
#<hash-table 0/31>
guile> (hashq-set! h 'key1 "val1")
"val1"
guile> (hashq-set! h 'key2 "val2")
"val2"
guile> (hashq-set! h 3 "val3")
"val3"

Werte werden aus den Hash-Tabellen mit hashq-ref ausgelesen.

 
guile> (hashq-ref h 3)
"val3"
guile> (hashq-ref h 'key2)
"val2"
guile>

Schlüssel und Werte werden als Paar mit hashq-get-handle ausgelesen. Das ist die beste Art, weil hier #f ausgegeben wird, wenn ein Schlüssel nicht gefunden werden kann.

 
guile> (hashq-get-handle h 'key1)
(key1 . "val1")
guile> (hashq-get-handle h 'frob)
#f
guile>

[ < ] [ > ]   [ << ] [ Nach oben ] [ >> ]         [Anfang] [Inhalt] [Index] [ ? ]

1.1.5 Berechnungen in Scheme

@translationof Calculations in Scheme

Scheme kann verwendet werden, um Berechnungen durchzuführen. Es verwendet eine Präfix-Syntax. Um 1 und 2 zu addieren, muss man (+ 1 2) schreiben, und nicht 1+2, wie in traditioneller Mathematik.

 
guile> (+ 1 2)
3

Berechnungen können geschachtelt werden und das Ergebnis einer Berechnung kann für eine neue Berechnung eingesetzt werden.

 
guile> (+ 1 (* 3 4))
13

Diese Berechnungen sind Beispiele von Auswertungen. Ein Ausdruck wie (* 3 4) wird durch seinen Wert 12 ersetzt.

Scheme-Berechnungen können zwischen Integren und Nich-Integren unterscheiden. Integre Berechnungen sind exakt, während Nicht-Integre nach den passenden Genauigkeitseinschränkungen berechnet werden:

 
guile> (/ 7 3)
7/3
guile> (/ 7.0 3.0)
2.33333333333333

Wenn der Scheme-Berechner einen Ausdruck antrifft, der eine Liste darstellt, wird das erste Element der Liste als Prozedur behandelt, die mit Argumenten des Restes der Liste ausgewertet werden. Darum sind alle Operatoren in Scheme vorangestellt.

Wenn das erste Element eines Scheme-Ausdrucks, der eine Liste darstellt, kein Operator oder keine Prozedur ist, gibt es einen Fehler:

 
guile> (1 2 3)

Backtrace:
In current input:
  52: 0* [1 2 3]

<unnamed port>:52:1: In expression (1 2 3):
<unnamed port>:52:1: Wrong type to apply: 1
ABORT: (misc-error)
guile>

Hier kann man sehen, dass Scheme versucht hat, 1 als einen Operator oder eine Prozedur zu behandeln, was aber nicht möglich war. Darum der Fehler "Wrong type to apply: 1".

Wenn man also eine Liste erstellen will, braucht man also einen Listen-Operator oder man muss die Liste als wörtliches Zitat schreiben, sodass Scheme sie nicht auszuwerten versucht.

 
guile> (list 1 2 3)
(1 2 3)
guile> '(1 2 3)
(1 2 3)
guile>

Dieser Fehler kann durchaus vorkommen, wenn man Scheme unter LilyPond einsetzt.


[ < ] [ > ]   [ << ] [ Nach oben ] [ >> ]         [Anfang] [Inhalt] [Index] [ ? ]

1.1.6 Scheme-Prozeduren

@translationof Scheme procedures

Scheme-Prozeduren sind ausführbare Scheme-Ausdrücke, die einen Wert ausgeben, der das Resultat ihrer Ausführung darstellt. Sie können auch Variablen verändern, die außerhalb dieser Prozedur definiert wurden.

Prozeduren definieren

Prozeduren werden in Scheme mit define definiert:

 
(define (function-name arg1 arg2 ... argn)
 scheme-expression-that-gives-a-return-value)

Beispielsweise könnte man eine Prozedur definieren, die den Durchschnitt berechnet:

 
guile> (define (average x y) (/ (+ x y) 2))
guile> average
#<procedure average (x y)>

Wenn die Prozedur einmal definiert ist, wird sie aufgerufen indem man die Prozedur und die Argumente in eine Liste schreibt. Man könnte also den Durchschnitt von 3 und 12 berechnen:

 
guile> (average 3 12)
15/2

Prädikate

Scheme-Prozeduren, die Boolsche Werte ausgeben, werden oft als Prädikate (predicate) bezeichnet. Es herrscht die Übereinkunft, Prädikat-Bezeichnungen mit einem Fragezeichen abzuschließen:

 
guile> (define (less-than-ten? x) (< x 10))
guile> (less-than-ten? 9)
#t
guile> (less-than-ten? 15)
#f

Wiedergabe-Werte

Scheme-Prozeduren geben immer einen Wiedergabe-Wert (return value) aus, welcher der Wert des letzten Ausdrucks ist, der in der Prozedur ausgeführt wurde. Der Wiedergabewert kann ein beliebiger gültiger Scheme-Wert sein, auch eine komplexe Datenstruktur oder eine Prozedur.

Manchmal würden Benutzer gerne mehrere Scheme-Ausdrucke in einer Prozedur haben. Es gibt zwei Arten, wie merhere Ausdrücke kombiniert werden können. Die erste Art ist die begin-Prozedur, der es ermöglicht, dass mehrere Ausdrücke ausgewertet werden und den Wert des letzten Ausdrucks wiedergibt.

 
guile> (begin (+ 1 2) (- 5 8) (* 2 2))
4

Die andere Art, mehrere Ausdrücke zu kombinieren, ist eine let-Umgebung. In dieser Umgebung wird eine Serie von Verbindungen erstellt, und dann wird eine Sequenz von Ausdrücken ausgewertet, die diese Bindungen einschließen können. Der Wiedergabewert der let-Umgebung ist der Wiedergabewert der letzten Aussage in der let-Umgebung:

 
guile> (let ((x 2) (y 3) (z 4)) (display (+ x y)) (display (- z 4))
... (+ (* x y) (/ z x)))
508

[ < ] [ > ]   [ << ] [ Nach oben ] [ >> ]         [Anfang] [Inhalt] [Index] [ ? ]

1.1.7 Scheme-Konditionale

@translationof Scheme conditionals

if

Scheme hat eine if-Prozedur:

 
(if test-expression true-expression false-expression)

test-expression ist ein Ausdruck, der einen Booleschen Wert zurück gibt. Wenn test-expression den Wert #t ausgibt, gibt die if-Prozedur den Wert von true-expression aus, in allen anderen Fällen den Wert von false-expression.

 
guile> (define a 3)
guile> (define b 5)
guile> (if (> a b) "a is greater than b" "a is not greater than b")
"a is not greater than b"

cond

Eine andere konditionale Prozedur in Scheme ist cond:

 
(cond (test-expression-1 result-expression-sequence-1)
      (test-expression-2 result-expression-sequence-2)
      ...
      (test-expression-n result-expression-sequence-n))

Beispielsweise:

 
guile> (define a 6)
guile> (define b 8)
guile> (cond ((< a b) "a is less than b")
...          ((= a b) "a equals b")
...          ((> a b) "a is greater than b"))
"a is less than b"

[ < ] [ > ]   [ << ] [ Nach oben ] [ >> ]

Dieses Dokument wurde erzeugt von gub am 13. Juli 2020 durch texi2html 1.82.

Andere Sprachen: English, español, français.
Über die automatische Sprachauswahl.