| [ < ] | [ > ] | [ << ] | [ Nach oben ] | [ >> ] | [Anfang] | [Inhalt] | [Index] | [ ? ] |
@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.
| 1.1.1 Scheme-Sandkasten | ||
| 1.1.2 Scheme-Variablen | ||
| 1.1.3 Einfache Scheme-Datentypen | ||
| 1.1.4 Zusammengesetzte Scheme-Datentypen | ||
| 1.1.5 Berechnungen in Scheme | ||
| 1.1.6 Scheme-Prozeduren | ||
| 1.1.7 Scheme-Konditionale |
| [ < ] | [ > ] | [ << ] | [ Nach oben ] | [ >> ] | [Anfang] | [Inhalt] | [Index] | [ ? ] |
@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] | [ ? ] |
@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] | [ ? ] |
@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.
Werte einer Booleschen Variable sind Wahr oder Falsch. Die Scheme-Entsprechung
für Wahr ist #t und für Falsch #f.
Zahlen werden wie üblich eingegeben, 1 ist die (ganze)
Zahl Eins, während -1.5 eine Gleitkommazahl (also
eine nicht-ganze) ist.
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] | [ ? ] |
@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.
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
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.
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.
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] | [ ? ] |
@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] | [ ? ] |
@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 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 |
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 |
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] | [ ? ] |
@translationof Scheme conditionals
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" |
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.