@c -*- coding: utf-8; mode: texinfo; documentlanguage: de -*- @ignore Translation of GIT committish: e5a609e373eae846857f9a6d70a402a3d42b7d94 When revising a translation, copy the HEAD committish of the version that you are working on. For details, see the Contributors' Guide, node Updating translation committishes.. @end ignore @c \version "2.16.0" @node Scheme-Übung @chapter Scheme-Übung @translationof Scheme tutorial @cindex Scheme @cindex GUILE @cindex Scheme, in einer LilyPond-Datei @cindex LISP @cindex Auswertung von Scheme-Code LilyPond verwendet die Scheme-Programmiersprache sowohl als Teil der Eingabesyntax als auch als internen Mechanismus, um Programmmodule zusammenzufügen. Dieser Abschnitt ist ein sehr kurzer Überblick über die Dateneingabe mit Scheme. Wenn Sie mehr über Scheme wissen wollen, gehen Sie zu @uref{http://@/www@/.schemers@/.org}. LilyPond benutzt die GNU Guile-Implementation von Scheme, die auf dem @qq{R5RS}-Standard von Scheme basiert. Wenn Sie Scheme lernen wollen, um es innerhalb von LilyPond zu benutzen, wird es nicht empfohlen, mit einer anderen Implementation (die sich auf einen anderen Standard bezieht) zu arbeiten. Information zu Guile findet sich unter @uref{http://www.gnu.org/software/guile/}. Der @qq{R5RS}-Standard von Scheme befindet sich unter der Adresse @uref{http://www.schemers.org/Documents/Standards/R5RS/}. @menu * Einleitung in Scheme:: * Scheme in LilyPond:: * Komplizierte Funktionen erstellen:: @end menu @node Einleitung in Scheme @section 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. @menu * Scheme-Sandkasten:: * Scheme-Variablen:: * Einfache Scheme-Datentypen:: * Zusammengesetzte Scheme-Datentypen:: * Berechnungen in Scheme:: * Scheme-Prozeduren:: * Scheme-Konditionale:: @end menu @node Scheme-Sandkasten @subsection 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 @code{guile} aufruft. Unter einigen Systemen, insbesondere unter Windows, muss man evtl. die Umgebungsvariable @code{GUILE_LOAD_PATH} auf das Verzeichnis @code{../usr/share/guile/1.8} innerhalb des LilyPond-Installationsverzeichnisses setzen (der vollständige Pfad ist erklärt in @rlearning{Mehr Information}). Alternativ können Windows-Benutzer auch einfach @qq{Ausführen} im Startmenü wählen und @code{guile} schreiben. Es gibt auch eine direkte Scheme-Umgebung mit allen LilyPond-Voreinstellungen, die man auf der Kommandozeile mit folgendem Befehl aufrufen kann: @example lilypond scheme-sandbox @end example @noindent Wenn guile einmal läuft, erhält man die Eingabeaufforderung von guile: @lisp guile> @end lisp Man kann Scheme-Ausdrucke hier eingeben und mit Scheme experimentieren. Siehe die Datei @file{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. @node Scheme-Variablen @subsection Scheme-Variablen @translationof Scheme variables Scheme-Variablen können jedlichen gültigen Scheme-Wert erhalten, auch Scheme-Prozeduren. Scheme-Variablen werden mit @code{define} definiert: @lisp guile> (define a 2) guile> @end lisp Scheme-Variablen können an der Guile-Eingabeaufforderung ausgewertet werden, indem man einfach die Variable eintippt. @lisp guile> a 2 guile> @end lisp Scheme-Variablen können auf dem Bildschirm ausgegeben werden, indem man @code{display} zum Anzeigen benutzt: @lisp guile> (display a) 2guile> @end lisp Sowohl der Wert @code{2} als auch die Eingabeaufforderung @code{guile} werden auf der gleichen Zeile ausgegeben. Das kann man vermeiden, indem man eine @code{newline}-Prozedur für eine Leerzeile aufruft oder das Zeichen für eine neue Zeile anzeigen lässt: @lisp guile> (display a)(newline) 2 guile> (display a)(display "\n") 2 guile> @end lisp Wenn eine Variable einmal erstellt wurde, kann ihr Wert durch @code{set!} verändert werden: @lisp guile> (set! a 12345) guile> a 12345 guile> @end lisp @node Einfache Scheme-Datentypen @subsection 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. @table @asis @item Boolesche Variablen Werte einer Booleschen Variable sind Wahr oder Falsch. Die Scheme-Entsprechung für Wahr ist @code{#t} und für Falsch @code{#f}. @funindex ##t @funindex ##f @item Zahlen Zahlen werden wie üblich eingegeben, @code{1} ist die (ganze) Zahl Eins, während @w{@code{-1.5}} eine Gleitkommazahl (also eine nicht-ganze) ist. @item Zeichenketten Zeichenketten werden in doppelte Anführungszeichen gesetzt: @example "Das ist eine Zeichenkette" @end example Zeichenketten können über mehrere Zeilen reichen: @example "Das ist eine Zeichenkette" @end example @noindent 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 @code{\n} in die Zeichenkette aufnimmt. @example "das\nist eine\nmehrzeilige Zeichenkette" @end example Anführungszeichen und neue Zeilen können auch mit sogenannten Fluchtsequenzen eingefügt werden. Die Zeichenkette @code{a sagt "b"} wird wie folgt eingegeben: @example "a sagt \"b\"" @end example @end table Weitere zusätzliche Scheme-Datentypen, die hier nicht besprochen wurden, finden sich in einer vollständigen Liste der Scheme-Datentypen in der Guile-Referenzanleitung: @uref{http://www.gnu.org/software/guile/manual/html_node/Simple-Data-Types.html}. @node Zusammengesetzte Scheme-Datentypen @subsection 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. @subheading Paare (pair) Der wichtigeste zusammengesetzte Datentyp in Scheme ist ein Paar (@code{pair}). Wie aus dem Namen schon hervorgeht, besteht ein Paar aus zwei zusammengeschweißten Werten. Die Prozedur um ein Paar zu erstellen ist @code{cons}. @lisp guile> (cons 4 5) (4 . 5) guile> @end lisp Das Paar wird dargestellt als zwei Elemente, von Klammern umgeben und durch Leerzeichen und einen Punkt (@code{.}) getrennt. Der Punkt ist @emph{kein} Dezimalpunkt, sondern markiert die Gruppe als Paar. Paare können auch wörtlich eingegeben werden, indem man ihnen voraus ein einfaches Anführungszeichen (@code{'} setzt. @lisp guile> '(4 . 5) (4 . 5) guile> @end lisp Beide Elemente eines Paares können beliebige gültige Scheme-Werte sein: @lisp guile> (cons #t #f) (#t . #f) guile> '("blah-blah" . 3.1415926535) ("blah-blah" . 3.1415926535) guile> @end lisp Das erste Element eines Paares kann mit der Prozedur @code{car}, das zweite mit der Prozedur @code{cdr} angesprochen werden. @lisp guile> (define mypair (cons 123 "hello there") ... ) guile> (car mypair) 123 guile> (cdr mypair) "hello there" guile> @end lisp @noindent Achtung: @code{cdr} wird ausgeprochen wie "kudd-err", nach Sussman und Abelson, siehe @uref{http://mitpress.mit.edu/sicp/full-text/book/book-Z-H-14.html#footnote_Temp_133} @subheading Listen (list) Ein sehr häufiger Datentyp in Scheme ist die Liste (@emph{list}). Formal gesehen wird eine Liste entweder als leere Liste definiert (repräsentiert als @code{'()}), oder als ein Paar, dessen @code{cdr} eine Liste ist. Es gibt viele Arten, Listen zu erstellen. Die vielleicht häufigste Methode ist die @code{list}-Prozedur: @lisp guile> (list 1 2 3 "abc" 17.5) (1 2 3 "abc" 17.5) @end lisp 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: @lisp guile> '(17 23 "foo" "bar" "bazzle") (17 23 "foo" "bar" "bazzle") @end lisp Listen haben eine zentrale Stellung in Scheme. Scheme wird als Dialekt von Lisp angesehen, und das Wort @qq{lisp} steht für @qq{List Processing}. Scheme-Ausdrücke sind immer Listen. @subheading Assoziative Listen (alist) Eine besonderer Listentyp ist die @emph{assoziative Liste} oder @emph{alist}. Eine Alist wird benutzt, um Daten zum einfachen Abrufen zu speichern. Alisten sind Listen, deren Elemente als Paare kommen. Der @code{car}-Teil jedes Elements wird als @emph{Schlüssel} (key) bezeichnet, der @code{cdr}-Teil jedes Elements wird @emph{Wert} (value) genannt. Die Scheme-Prozedur @code{assoc} wird benutzt, um einen Eintrag aus einer Aliste aufzurufen, und mit @code{cdr} wird sein Wert abgefragt: @lisp 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> @end lisp Alisten werden sehr viel in LilyPond genutzt, um Eigenschaften und andere Daten zu speichern. @subheading 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. @lisp guile> (define h (make-hash-table 10)) guile> h # guile> (hashq-set! h 'key1 "val1") "val1" guile> (hashq-set! h 'key2 "val2") "val2" guile> (hashq-set! h 3 "val3") "val3" @end lisp Werte werden aus den Hash-Tabellen mit @code{hashq-ref} ausgelesen. @lisp guile> (hashq-ref h 3) "val3" guile> (hashq-ref h 'key2) "val2" guile> @end lisp Schlüssel und Werte werden als Paar mit @code{hashq-get-handle} ausgelesen. Das ist die beste Art, weil hier @code{#f} ausgegeben wird, wenn ein Schlüssel nicht gefunden werden kann. @lisp guile> (hashq-get-handle h 'key1) (key1 . "val1") guile> (hashq-get-handle h 'frob) #f guile> @end lisp @node Berechnungen in Scheme @subsection Berechnungen in Scheme @translationof Calculations in Scheme @ignore We have been using lists all along. A calculation, like @code{(+ 1 2)} is also a list (containing the symbol @code{+} and the numbers 1 and@tie{}2). Normally lists are interpreted as calculations, and the Scheme interpreter substitutes the outcome of the calculation. To enter a list, we stop the evaluation. This is done by quoting the list with a quote @code{'} symbol. So, for calculations do not use a quote. Inside a quoted list or pair, there is no need to quote anymore. The following is a pair of symbols, a list of symbols and a list of lists respectively, @example #'(stem . head) #'(staff clef key-signature) #'((1) (2)) @end example @end ignore Scheme kann verwendet werden, um Berechnungen durchzuführen. Es verwendet eine @emph{Präfix}-Syntax. Um 1 und@tie{}2 zu addieren, muss man @code{(+ 1 2)} schreiben, und nicht @math{1+2}, wie in traditioneller Mathematik. @lisp guile> (+ 1 2) 3 @end lisp Berechnungen können geschachtelt werden und das Ergebnis einer Berechnung kann für eine neue Berechnung eingesetzt werden. @lisp guile> (+ 1 (* 3 4)) 13 @end lisp Diese Berechnungen sind Beispiele von Auswertungen. Ein Ausdruck wie @code{(* 3 4)} wird durch seinen Wert @code{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: @lisp guile> (/ 7 3) 7/3 guile> (/ 7.0 3.0) 2.33333333333333 @end lisp 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, @emph{kein} Operator oder keine Prozedur ist, gibt es einen Fehler: @lisp guile> (1 2 3) Backtrace: In current input: 52: 0* [1 2 3] :52:1: In expression (1 2 3): :52:1: Wrong type to apply: 1 ABORT: (misc-error) guile> @end lisp 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. @lisp guile> (list 1 2 3) (1 2 3) guile> '(1 2 3) (1 2 3) guile> @end lisp Dieser Fehler kann durchaus vorkommen, wenn man Scheme unter LilyPond einsetzt. @ignore The same assignment can be done in completely in Scheme as well, @example #(define twentyFour (* 2 twelve)) @end example @c this next section is confusing -- need to rewrite The @emph{name} of a variable is also an expression, similar to a number or a string. It is entered as @example #'twentyFour @end example @funindex #'symbol @cindex quoting in Scheme The quote mark @code{'} prevents the Scheme interpreter from substituting @code{24} for the @code{twentyFour}. Instead, we get the name @code{twentyFour}. @end ignore @node Scheme-Prozeduren @subsection 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. @subheading Prozeduren definieren Prozeduren werden in Scheme mit @code{define} definiert: @example (define (function-name arg1 arg2 ... argn) scheme-expression-that-gives-a-return-value) @end example Beispielsweise könnte man eine Prozedur definieren, die den Durchschnitt berechnet: @lisp guile> (define (average x y) (/ (+ x y) 2)) guile> average # @end lisp 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: @lisp guile> (average 3 12) 15/2 @end lisp @subheading 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: @lisp guile> (define (less-than-ten? x) (< x 10)) guile> (less-than-ten? 9) #t guile> (less-than-ten? 15) #f @end lisp @subheading 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 @code{begin}-Prozedur, der es ermöglicht, dass mehrere Ausdrücke ausgewertet werden und den Wert des letzten Ausdrucks wiedergibt. @lisp guile> (begin (+ 1 2) (- 5 8) (* 2 2)) 4 @end lisp Die andere Art, mehrere Ausdrücke zu kombinieren, ist eine @code{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: @lisp guile> (let ((x 2) (y 3) (z 4)) (display (+ x y)) (display (- z 4)) ... (+ (* x y) (/ z x))) 508 @end lisp @node Scheme-Konditionale @subsection Scheme-Konditionale @translationof Scheme conditionals @subheading if Scheme hat eine @code{if}-Prozedur: @example (if test-expression true-expression false-expression) @end example @var{test-expression} ist ein Ausdruck, der einen Booleschen Wert zurück gibt. Wenn @var{test-expression} den Wert @code{#t} ausgibt, gibt die if-Prozedur den Wert von @var{true-expression} aus, in allen anderen Fällen den Wert von @var{false-expression}. @lisp 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" @end lisp @subheading cond Eine andere konditionale Prozedur in Scheme ist @code{cond}: @example (cond (test-expression-1 result-expression-sequence-1) (test-expression-2 result-expression-sequence-2) ... (test-expression-n result-expression-sequence-n)) @end example Beispielsweise: @lisp 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" @end lisp @node Scheme in LilyPond @section Scheme in LilyPond @translationof Scheme in LilyPond @menu * LilyPond Scheme-Syntax:: * LilyPond-Variablen:: * Eingabe-Variablen und Scheme:: * Scheme in LilyPond importieren:: * Objekteigenschaften:: * Zusammengesetzte LilyPond-Variablen:: * Interne musikalische Repräsentation:: @end menu @node LilyPond Scheme-Syntax @subsection LilyPond Scheme-Syntax @translationof LilyPond Scheme syntax @funindex $ @funindex # Der Guile-Auswerter ist ein Teil von LilyPond, sodass Scheme also auch in normale LilyPond-Eingabedateien eingefügt werden kann. Es gibt mehrere Methoden, um Scheme in LilyPond zu integrieren. Die einfachste Weise ist es, ein Rautenzeichen@tie{}@code{#} vor einem Scheme-Ausdruck zu benutzen. Die Eingabe von LilyPond ist in Zeichen und Ausdrücke gegliedert, so etwa wie die menschliche Sprache sich in Wörter und Sätze gliedert. LilyPond hat einen Lexer, der Zeichen erkennt (Zahlen, Zeichenketten, Scheme-Elemente, Tonhöhen usw.) und einen Parser, der die Syntax versteht, @ruser{LilyPond grammar}. Wenn dann eine bestimmte Syntaxregel als zuständig erkannt wurde, werden die damit verknüpften Aktionen ausgeführt. Die Rautenzeichenmethode (@code{#}), mit der Scheme eingebettet werden kann, passt sehr gut in dieses System. Wenn der Lexer ein Rautenzeichen sieht, ruft er den Scheme-reader auf, um den ganzen Scheme-Ausdruck zu lesen (das kann eine Variable, ein Ausdruck in Klammern oder verschiedene andere Sachen sein). Nachdem der Scheme-Ausdruck gelesen wurde, wird er als Wert eines @code{SCM_TOKEN} in der Grammatik gespeichert. Wenn der Parser weiß, wie er diesen Wert benutzen kann, ruft er Guile auf, um den Scheme-Ausdruck auszuwerten. Weil der Parser normalerweise dem Lexer etwas voraus sein muss, ist die Trennung von Lesen und Auswerten zwischen Lexer und Parser genau das richtige, um die Auswertung von LilyPond- und Scheme-Ausdrücken synchron zu halten. Aus diesem Grund sollte das Rautenzeichen zum Einbinden von Scheme immer benutzt werden, wenn es möglich ist. Eine andere Möglichkeit, Scheme aufzurufen, ist die Benutzung des Dollarzeichens (@code{$}) anstelle der Raute. In diesem Fall wertet LilyPond den Code sofort aus, nachdem der Lexer ihn gelesen hat. Dabei wird der resultierende Scheme-Ausdruckstyp geprüft und eine Tokentyp dafür ausgesucht (einer von mehreren @code{xxx_IDENTIFIER} in der Syntax). Wenn der Wert des Ausdrucks gültig ist (der Guilde-Wert für @code{*unspecified*}), dann wird nichts an den Parser übergeben. Das ist auch der gleiche Mechanismus, nach dem LilyPond funktioniert, wenn man eine Variable oder musikalische Funktion mit ihrer Bezeichnung ausruft, wie in @code{\Bezeichnung}, mit dem einzigen Unterschied, dass ihre Bezeichnung durch den LilyPond-Lexer bestimmt wird, ohne den Scheme-reader einzubeziehen, und also nur Variablen akzeptiert werden, die im aktuellen LilyPond-Modus gültig sind. Die direkte Auswirkung von @code{$} kann zu Überraschungen führen, siehe @ref{Eingabe-Variablen und Scheme}. Es bietet sich daher an, @code{#} immer zu benützen, wenn der Parser es unterstützt. Innerhalb von musikalischen Ausdrücken werden Ausdrücke, die mit @code{#} erstellt werden, @emph{tatsächlich} als Noten interpretiert. Sie werden jedoch @emph{nicht} vor der Benutzung kopiert. Wenn Sie Teil einer Struktur sind, die noch einmal benutzt werden soll, muss man eventuell @code{ly:music-deep-copy} explizit einsetzen. @funindex $@@ @funindex #@@ Es gibt auch die Operatoren @code{$@@} und @code{#@@}, die eine @qq{listentrennende} Funktion aufweisen, indem sie alle Elemente einer Liste in den umgebenden Kontext einfügen. Jetzt wollen wir uns tatsächlichen Scheme-Code anschauen. Scheme-Prozeduren können in LilyPond-Eingabedateien definiert werden: @example #(define (average a b c) (/ (+ a b c) 3)) @end example LilyPond-Kommentare (@code{%} oder @code{%@{ %@}}) können innerhalb von Scheme-Code nicht benutzt werden, nicht einmal in einer LilyPond-Eingabedatei, weil der Guile-Interpreter und nicht der LilyPond-Parser den Scheme-Ausdruck liest. Kommentare in Guile Scheme werden wie folgt notiert: @example ; Einzeiliges Kommentar #! Guile-Stil Blockkommentar (nicht schachtelbar) Diese Kommentare werden von Scheme-Programmierern selten benutzt und nie im Quellcode von LilyPond !# @end example Für den Rest dieses Abschnitts soll angenommen werden, dass die Daten in einer LilyPond-Eingabedatei notiert werden sollen, sodass immer@tie{}@code{#} vor die Scheme-Ausdrücke gestellt wird. Alle Scheme-Ausdrücke auf oberster Ebene in einer LilyPond-Eingabedatei können in einen einzigen Scheme-Ausdruck zusammengefasst werden mit @code{begin}: @example #(begin (define foo 0) (define bar 1)) @end example @node LilyPond-Variablen @subsection LilyPond-Variablen @translationof LilyPond variables LilyPond-Variablen werden intern als Scheme-Variablen gespeichert. Darum ist @example Zwoelf = 12 @end example @noindent das Gleiche wie @example #(define Zwoelf 12) @end example Das bedeutet, dass Scheme-Ausdrücke auf LilyPond-Variablen zugreifen können. Man könnte also schreiben: @example Vierundzwanzig = #(* 2 Zwoelf) @end example @noindent was zur Folge hätte, dass die Zahl 24 in der LilyPond (und Scheme-)Variablen @code{Vierundzwanzig} gespeichert wird. Üblicherweise greift man auf LilyPond-Variablen zu, indem man ihnen einen Backslash voranstellt. Siehe auch @ref{LilyPond Scheme-Syntax}, also etwa @code{\Vierundzwanzig}. Weil dadurch eine Kopie des Wertes für die meisten von LilyPonds internen Typen erstellt wird (insbesondere musikalische Funktionen), erstellen musikalische Funktionen normalerweise Kopien von Material, das sie verändern. Aus diesem Grund sollten musikalische Funktionen, die mit @code{#} aufgerufen werden, kein Material enthalten, dass entweder von Grund auf neu erstellt wird oder explizit kopiert wird, sondern besser direkt auf das relevante Material verweisen. @node Eingabe-Variablen und Scheme @subsection Eingabe-Variablen und Scheme @translationof Input variables and Scheme Das Eingabeformat unterstützt die Notation von Variablen: Im folgenden Beispiel wird ein musikalischer Ausdruck einer Variablen mit der Bezeichnung @code{traLaLa} zugewiesen: @example traLaLa = @{ c'4 d'4 @} @end example Variablen haben beschränkte Geltungsbereiche: im unten stehenden Beispiel enthält auch die @code{\layout}-Umgebung eine @code{traLaLa}-Variable, die sich aber von der @code{traLaLa}-Variable auf oberster Ebene unterscheidet: @example traLaLa = @{ c'4 d'4 @} \layout @{ traLaLa = 1.0 @} @end example Jede Eingabedatei ist solch ein Gültigkeitsbereich, und alle @code{\header}-, @code{\midi}- und @code{\layout}-Umgebungen sind Gültigkeitsbereiche, die in diesen obersten Gültigkeitsbereich eingebettet sind. Sowohl Variablen als auch Gültigkeitsbereiche sind im Guile Modulsystem eingebaut. Ein anonymes Scheme-Modul wird jedem Gültigkeitsbereich angehängt. Eine Zuweisen in der Form von @example traLaLa = @{ c'4 d'4 @} @end example @noindent wird intern in die Scheme-Definition @example (define traLaLa @var{Scheme-Wert von `@code{... }'}) @end example @noindent konvertiert. Das bedeutet, dass LilyPond-Variablen und Scheme-Variablen frei gemischt werden können. Im folgenden Beispiel wird ein Notenfragment in der Variable @code{traLaLa} gespeichert und mit Scheme dupliziert. Das Resultat wird in eine @code{\score}-Umgebung mit einer weiteren Variable @code{twice} importiert: @lilypond[verbatim] traLaLa = { c'4 d'4 } #(define newLa (map ly:music-deep-copy (list traLaLa traLaLa))) #(define twice (make-sequential-music newLa)) \twice @end lilypond Das ist ein interessantes Beispiel. Die Zuweisung findet erst statt, nachdem der Parser sichergestellt hat, dass nichts folgt, das Ähnlichkeit mit @code{\addlyrics} hat, sodass er prüfen muss, was als nächstes kommt. Er liest @code{#} und den darauf folgenden Scheme-Ausdruck, @emph{ohne} ihn auszuwerten, so dass er weiterlesen und erst @emph{danach} wird der Scheme-Code ohne Probleme ausführen kann. @node Scheme in LilyPond importieren @subsection Scheme in LilyPond importieren @translationof Importing Scheme in LilyPond @funindex $ @funindex # Das Beispiel zeigt, wie man musikalische Ausdrücke aus der Eingabe in den Scheme-Auswerter @qq{exportieren} kann. Es geht auch andersherum. Indem man Scheme-Werte nach @code{$} schreibt, wird ein Scheme-Wert interpretiert, als ob er in LilyPond-Syntax eingeben wäre. Anstatt @code{\twice} zu definieren, könne man also auch schreiben: @example ... $(make-sequential-music newLa) @end example Mann kann @code{$} zusammen mit einem Scheme-Ausdruck überall benutzen, wo auch @code{\@var{Bezeichnung}} gültig wäre, nachdem der Scheme-Ausdruck einmal einer Variable @var{Bezeichnung} zugewiesen worden ist. Der Austausch geschieht im Lexer, sodass LilyPond den Unterschied gar nicht merkt. Ein negativer Effekt ist aber das Timing. Wenn man @code{$} anstelle von @code{#} für die Definition von @code{newLa} im obigen Beispiel eingesetzt hätte, würde der folgende Scheme-Ausdruck fehlschlagen, weil @code{traLaLa} noch nicht definiert worden wäre. Zu einer Erklärung dieses Timingproblems siehe @ref{LilyPond Scheme-Syntax}. @funindex $@@ @funindex #@@ Eine weitere Bequemlichkeit können die @qq{listentrennenden} Operatoren @code{$@@} und @code{#@@} bieten, indem sie die Elemente einer Liste in den umgebenden Kontext einfügen. Wenn man sie einsetzt, hätte der letzte Teil des Beispiels auch so geschrieben werden können: @example ... @{ #@@newLa @} @end example Hier wird jedes Element der Liste, welche in @code{newLa} gespeichert ist, der Reihenfolge nach genommen und in die Liste eingefügt, als ob man geschrieben hätte: @example @{ #(first newLa) #(second newLa) @} @end example In allen diesen Formen findet die Auswertung des Scheme-Codes statt, während die Eingabe noch gelesen wird, entweder im Lexer oder im Parser. Wenn man es später ausgeführt haben möchte, muss man @ref{Leere Scheme-Funktionen} benutzen oder es in einem Makro speichern: @example #(define (nopc) (ly:set-option 'point-and-click #f)) ... #(nopc) @{ c'4 @} @end example @knownissues Scheme- und LilyPond-Variablen können nicht gemischt werden, wenn man die @option{--safe}-Option benutzt. @node Objekteigenschaften @subsection Objekteigenschaften @translationof Object properties Objekteigenschaften werden in LilyPond in Form von Alisten-Ketten gespeichert, also als Listen von Alisten. Eigenschaften werden geändert, indem man Werte an den Anfang der Eigenschaftsliste hinzufügt. Eigenschaften werden gelesen, indem Werte aus der Aliste gelesen werden. Ein neuer Wert für eine Eigenschaft kann gesetzt werden, indem man der Alist einen Wert mit Schlüssel und dem Wert zuweist. Die LilyPond-Syntax hierfür ist: @example \override Stem #'thickness = #2.6 @end example Diese Anweisung verändert die Erscheinung der Notenhälse. Der Alist-Eintrag @code{'(thickness . 2.6)} wird zu der Eigenschaftsliste eines @code{Stem}-(Hals-)Objektes hinzugefügt. @code{thickness} wird relativ zu den Notenlinien errechnet, in diesem Fall sind die Hälse also 2,6 mal so dick wie die Notenlinien. Dadurch werden Hälse fast zweimal so dick dargestellt, wie sie normalerweise sind. Um zwischen Variablen zu unterscheiden, die in den Quelldateien direkt definiert werden (wie @code{Vierundzwanzig} weiter oben), und zwischen denen, die für interne Objekte zuständig sind, werden hier die ersteren @qq{Variablen} genannt, die letzteren dagegen @qq{Eigenschaften}. Das Hals-Objekt hat also eine @code{thickness}-Eigenschaft, während @code{Vierundzwanzig} eine Variable ist. @cindex Eigenschaften versus Bezeichner @cindex Bezeichner versus Eigenschaften @node Zusammengesetzte LilyPond-Variablen @subsection Zusammengesetzte LilyPond-Variablen @translationof LilyPond compound variables @subheading Abstände (offset) Zweidimensionale Abstände (X- und Y-Koordinaten) werden als @emph{pairs} (Paare) gespeichert. Der @code{car}-Wert des Abstands ist die X-Koordinate und der @code{cdr}-Wert die Y-Koordinate. @example \override TextScript #'extra-offset = #'(1 . 2) @end example Hierdurch wird das Paar @code{(1 . 2)} mit der Eigenschaft @code{extra-offset} des TextScript-Objektes verknüpft. Diese Zahlen werden in Systembreiten gemessen, so dass der Befehl das Objekt eine Systembreite nach rechts verschiebt und zwei Breiten nach oben. Prozeduren, um mit Abständen zu arbeiten, finden sich in @file{scm/lily-library.scm}. @subheading Brüche (fractions) Brüche, wie sie LilyPond benutzt, werden wiederum als Paare gespeichert, dieses Mal als unbezeichnete ganze Zahlen. Während Scheme rationale Zahlen als einen negativen Typ darstellen kann, sind musikalische gesehen @samp{2/4} und @samp{1/2} nicht das selbe, sodass man zwischen beiden unterscheiden können muss. Ähnlich gibt es auch keine negativen Brüche in LilyPonds Sinn. Somit bedeutet @code{2/4} in LilyPond @code{(2 . 4)} in Scheme, und @code{#2/4} in LilyPond bedeutet @code{1/2} in Scheme. @subheading Bereiche (extend) Paare werden auch benutzt, um Intervalle zu speichern, die einen Zahlenbereich vom Minimum (dem @code{car}) bis zum Maximum (dem @code{cdr}) darstellen. Intervalle werden benutzt, um die X- und Y-Ausdehnung von druckbaren Objekten zu speichern. Bei X-Ausdehnungen ist @code{car} die linke X-Koordinate und @code{cdr} die rechte X-Koordinate. Für Y-Ausdehnungen ist @code{car} die untere Koordinate und @code{cdr} die obere Koordinate. Prozeduren, um mit Intervallen zu arbeiten, finden sich in @file{scm/lily-library.scm}. Diese Prozeduren sollten benutzt, wenn es möglich ist, um den Code konsistent zu halten. @subheading Eigenschafts-Alisten (property alist) Eine Eigenschafts-Aliste ist eine LilyPond-Datenstruktur, die eine Aliste darstellt, deren Schlüssel Eigenschaften sind und deren Werte Scheme-Ausdrücke sind, die den erwünschen Wert der Eigenschaft ausgeben. LilyPond-Eigenschaften sind Scheme-Symbole, wie etwa @code{'thickness} (Dicke). @subheading Alisten-Ketten (alist chains) Eine Alisten-Kette ist eine Liste, die Eigenschafts-Alisten enthält. Die Menge aller Eigenschaften, die sich auf einen Grob auswirken, wird typischerweise in einer Alisten-Kette gespeichert. Um den Wert einer bestimmten Eigenschaft zu finden, die ein Grob haben soll, wird jede Liste in der Kette nach einander durchsucht, wobei nach einem Eintrag geschaut wird, der den Eigenschaftsschlüssel enthält. Der erste gefundene Alisten-Eintrag wird benutzt und dessen Wert ist der Wert der Eigenschaft. Die Scheme-Prozedur @code{chain-assoc-get} wird normalerweise benutzt, um Grob-Eigenschaftenwerte zu erhalten. @node Interne musikalische Repräsentation @subsection Interne musikalische Repräsentation @translationof Internal music representation Intern werden Noten als Scheme-Liste dargestellt. Die Liste enthält verschiedene Elemente, die die Druckausgabe beeinflussen. Parsen nennt man den Prozess, der die Noten aus der LilyPond-Repräsentation in die interne Scheme-Repräsentation überführt. Wenn ein musikalischer Ausdruck geparst wird, wird er in eine Gruppe von Scheme-Musikobjekten konvertiert. Die definierende Eigenschaft eines Musikobjektes ist, dass es Zeit einnimmt. Die Zeit, die ein Objekt braucht, wird Dauer (engl. @emph{duration}) genannt. Dauern werden in rationalen Zahlen ausgedrückt, die die Länge des Musikobjekts in Ganzen Noten angeben. Ein Musikobjekt hat drei Typen: @itemize @item Musikbezeichnung (music name): Jeder Musikausdruck hat eine Bezeichnung. Eine Note beispielsweise erzeugt ein @rinternals{NoteEvent} und @code{\simultaneous} produziert @rinternals{SimultaneousMusic}. Eine Liste aller möglichen Ausdrücke findet sich in der Referenz der Interna, unter @rinternals{Music expressions}. @item Typ (type) oder Schnittstelle (interface): Jede Musikbezeichnung hat mehrere Typen oder Schnittstellen, beispielsweise eine Note ist ein Ereignis (@code{event}), aber auch ein Notenereignis (@code{note-event}), ein rhythmisches Ereignis (@code{rhythmic-event}) und ein Melodieereignis (@code{melodic-event}). Alle Musikklassen sind in der Referenz der Interna aufgelistet, unter @rinternals{Music classes}. @item C++-Objekt: Jedes Musikobjekt ist durch ein Objekt der C++-Klasse @code{Music} repräsentiert. @end itemize Die eigentliche Information eines musikalischen Ausdrucks wird in Eigenschaften gespeichert. Ein @rinternals{NoteEvent} beispielsweise hat die Eigenschaften Tonhöhe (@code{pitch}) und Dauer (@code{duration}), die die Dauer und die Tonhöhe der Note speichern. Eine Liste aller möglichen Eigenschaften findet sich in der Referenz der Interna, unter @rinternals{Music properties}. Ein zusammengesetzter musikalischer Ausdruck ist ein Musikobjekt, das andere Musikobjekte als Eigenschaften enthält. Eine Liste an Objekten kann in der @code{elements}-Eigenschaft eines Musikobjekts bzw. ein einziges Ableger-Musikelement in der @code{element}-Eigenschaft gespeichert werden. @rinternals{SequentialMusic} beispielsweise hat sein einziges Argument in @code{element}. Der Körper einer Wiederholung wird in der @code{element}-Eigenschaft von @rinternals{RepeatedMusic} gespeichert, und die alternativen Endungen in @code{elements}. @node Komplizierte Funktionen erstellen @section Komplizierte Funktionen erstellen @translationof Building complicated functions Dieser Abschnitt zeigt, wie man Information zusammensucht, um komplizierte musikalische Funktionen zu erstellen. @menu * Musikalische Funktionen darstellen:: * Eigenschaften von Musikobjekten:: * Verdoppelung einer Note mit Bindebögen (Beispiel):: * Artikulationszeichen zu Noten hinzufügen (Beispiel):: @end menu @node Musikalische Funktionen darstellen @subsection Musikalische Funktionen darstellen @translationof Displaying music expressions @cindex interne Speicherung @cindex Musikausdrücke anzeigen @cindex Anzeigen von Musikausdrücken @funindex displayMusic @funindex \displayMusic Wenn man eine musikalische Funktion erstellt, ist es oft hilfreich sich anzuschauen, wie musikalische Funktionen intern gespeichert werden. Das kann mit der Funktion @code{\displayMusic} erreicht werden: @example @{ \displayMusic @{ c'4\f @} @} @end example @noindent zeigt: @example (make-music 'SequentialMusic 'elements (list (make-music 'NoteEvent 'articulations (list (make-music 'AbsoluteDynamicEvent 'text "f")) 'duration (ly:make-duration 2 0 1 1) 'pitch (ly:make-pitch 0 0 0)))) @end example Normalerweise gibt LilyPond diese Ausgabe auf der Konsole mit allen anderen Nachrichten aus. Um die wichtigen Nachrichten in einer Datei zu speichern, kann die Ausgabe in eine Datei umgeleitet werden: @example lilypond file.ly >display.txt @end example Mit LilyPond- und Scheme-Magie kann man LilyPond anweisen, genau diese Ausgabe an eine eigene Datei zu senden: @example @{ #(with-output-to-file "display.txt" (lambda () #@{ \displayMusic @{ c'4\f @} #@})) @} @end example Mit etwas Umformatierung ist die gleiche Information sehr viel einfacher zu lesen: @example (make-music 'SequentialMusic 'elements (list (make-music 'NoteEvent 'articulations (list (make-music 'AbsoluteDynamicEvent 'text "f")) 'duration (ly:make-duration 2 0 1 1) 'pitch (ly:make-pitch 0 0 0)))) @end example Eine musikalische @code{@{ ... @}}-Sequenz hat die Bezeichnung @code{SequentialMusic} und ihre inneren Ausdrücke werden als Liste in seiner @code{'elements}-Eigenschaft gespeichert. Eine Note ist als als ein @code{EventChord}-Objekt dargestellt (welches Dauer und Tonhöhe speichert) und zusätzliche Information enthält (in diesem Fall ein @code{AbsoluteDynamicEvent} mit einer @code{"f"}-Text-Eigenschaft). @funindex \void @code{\displayMusic} gibt die Noten aus, die dargestellt werden, sodass sie sowohl angezeigt als auch ausgewertet werden. Um die Auswertung zu vermeiden, kann @code{\void} vor @code{\displayMusic} geschrieben werden. @node Eigenschaften von Musikobjekten @subsection Eigenschaften von Musikobjekten @translationof Music properties TODO -- make sure we delineate between @emph{music} properties, @emph{context} properties, and @emph{layout} properties. These are potentially confusing. Schauen wir uns ein Beispiel an: @example someNote = c' \displayMusic \someNote ===> (make-music 'NoteEvent 'duration (ly:make-duration 2 0 1 1) 'pitch (ly:make-pitch 0 0 0)) @end example Das @code{NoteEvent}-Objekt ist die Repräsentation von @code{someNote}. Einfach. Wie fügt man denn ein c' in einen Akkorde ein? @example someNote = \displayMusic \someNote ===> (make-music 'EventChord 'elements (list (make-music 'NoteEvent 'duration (ly:make-duration 2 0 1 1) 'pitch (ly:make-pitch 0 0 0)))) @end example Jetzt ist das @code{NoteEvent}-Objekt das erste Objekt der @code{'elements}-Eigenschaft von @code{someNote}. Die @code{display-scheme-music}-Funktion ist die Funktion, die von @code{\displayMusic} eingesetzt wird, um die Scheme-Repräsentation eines musikalischen Ausdrucks anzuzeigen. @example #(display-scheme-music (first (ly:music-property someNote 'elements))) ===> (make-music 'NoteEvent 'duration (ly:make-duration 2 0 1 1) 'pitch (ly:make-pitch 0 0 0)) @end example Danach wird die Tonhöhe der Note von der @code{'pitch}-Eigenschaft des @code{NoteEvent}-Objektes gelesen: @example #(display-scheme-music (ly:music-property (first (ly:music-property someNote 'elements)) 'pitch)) ===> (ly:make-pitch 0 0 0) @end example Die Tonhöhe einer Note kann geändert werden, indem man diese @code{'pitch}-Eigenschaft umdefiniert: @funindex \displayLilyMusic @funindex displayLilyMusic @example #(set! (ly:music-property (first (ly:music-property someNote 'elements)) 'pitch) (ly:make-pitch 0 1 0)) ;; Die Tonhöhen auf d' verändern. \displayLilyMusic \someNote ===> d' @end example @node Verdoppelung einer Note mit Bindebögen (Beispiel) @subsection Verdoppelung einer Note mit Bindebögen (Beispiel) @translationof Doubling a note with slurs (example) In diesem Abschnitt soll gezeigt, werden, wie man eine Funktion erstellt, die eine Eingabe wie @code{a} nach @code{@{ a( a) @}} umdefiniert. Dazu wird zuerst die interne Repräsentation der Musik betrachtet, die das Endergebnis darstellt: @example \displayMusic@{ a'( a') @} ===> (make-music 'SequentialMusic 'elements (list (make-music 'NoteEvent 'articulations (list (make-music 'SlurEvent 'span-direction -1)) 'duration (ly:make-duration 2 0 1 1) 'pitch (ly:make-pitch 0 5 0)) (make-music 'NoteEvent 'articulations (list (make-music 'SlurEvent 'span-direction 1)) 'duration (ly:make-duration 2 0 1 1) 'pitch (ly:make-pitch 0 5 0)))) @end example Eine schlechte Nachricht ist, dass die @code{SlurEvent}-Ausdrücke @qq{innerhalb} der Noten (in ihrer @code{articulations}-Eigenschaft) hinzugefügt werden müssen. Jetzt folgt eine Betrachtung der Eingabe: @example \displayMusic a' ===> (make-music 'NoteEvent 'duration (ly:make-duration 2 0 1 1) 'pitch (ly:make-pitch 0 5 0)))) @end example In der gewünschten Funktion muss also dieser Ausdruck kopiert werden (sodass zwei Noten vorhanden sind, die eine Sequenz bilden), dann müssen @code{SlurEvent} zu der @code{'articulations}-Eigenschaft jeder Noten hinzugefügt werden, und schließlich muss eine @code{SequentialMusic} mit den beiden @code{EventChords} erstellt werden. Um zu einer Eigenschaft etwas hinzuzufügen, ist es nützlich zu wissen, dass eine nicht gesetzte Eigenschaft als @code{'()} gelesen wird, sodass keine speziellen Überprüfungen nötig sind, bevor ein anderes Element vor die @code{articulations}-Eigenschaft gesetzt wird. @example doubleSlur = #(define-music-function (parser location note) (ly:music?) "Return: @{ note ( note ) @}. `note' is supposed to be a single note." (let ((note2 (ly:music-deep-copy note))) (set! (ly:music-property note 'articulations) (cons (make-music 'SlurEvent 'span-direction -1) (ly:music-property note 'articulations))) (set! (ly:music-property note2 'articulations) (cons (make-music 'SlurEvent 'span-direction 1) (ly:music-property note2 'articulations))) (make-music 'SequentialMusic 'elements (list note note2)))) @end example @node Artikulationszeichen zu Noten hinzufügen (Beispiel) @subsection Artikulationszeichen zu Noten hinzufügen (Beispiel) @translationof Adding articulation to notes (example) Am einfachsten können Artikulationszeichen zu Noten hinzugefügt werden, indem man zwei musikalische Funktionen in einen Kontext einfügt, wie erklärt in @ruser{Kontexte erstellen}. Hier soll jetzt eine musikalische Funktion entwickelt werden, die das vornimmt. Daraus ergibt sich der zusätzliche Vorteil, dass diese musikalische Funktion eingesetzt werden kann, um eine Artikulation (wie etwa eine Fingersatzanweisung) einer einzigen Note innerhalb eines Akkordes hinzugefügt werden kann, was nicht möglich ist, wenn einfach unabhängige Noten ein einem Kontext miteinander verschmolzen werden. Eine @code{$variable} innerhalb von @code{#@{...#@}} ist das gleiche wie die normale Befehlsform @code{\variable} in üblicher LilyPond-Notation. Es ist bekannt dass @example @{ \music -. -> @} @end example @noindent in LilyPond nicht funktioniert. Das Problem könnte vermieden werden, indem das Artikulationszeichen an einen leeren Akkord gehängt wird: @example @{ << \music <> -. -> >> @} @end example @noindent aber in diesem Beispiel soll gezeigt werden, wie man das in Scheme vornimmt. Zunächst wird die Eingabe und die gewünschte Ausgabe examiniert: @example % Eingabe \displayMusic c4 ===> (make-music 'NoteEvent 'duration (ly:make-duration 2 0 1 1) 'pitch (ly:make-pitch -1 0 0)))) ===== % gewünschte Ausgabe \displayMusic c4-> ===> (make-music 'NoteEvent 'articulations (list (make-music 'ArticulationEvent 'articulation-type "accent")) 'duration (ly:make-duration 2 0 1 1) 'pitch (ly:make-pitch -1 0 0)) \displayMusic c4 ===> (make-music 'EventChord 'elements (list (make-music 'NoteEvent 'duration (ly:make-duration 2 0 1 1) 'pitch (ly:make-pitch -1 0 0)))) @end example Dabei ist zu sehen, dass eine Note (@code{c4}) als @code{NoteEvent}-Ausdruck repräsentiert ist. Um eine Akzent-Artikulation hinzuzufügen, muss ein @code{ArticulationEvent}-Ausdruck zu der Elementeigenschaft @code{articulations} des @code{NoteEvent}-Ausdrucks hinzugefügt werden. Um diese Funktion zu bauen, wird folgendermaßen begonnen: @example (define (add-accent note-event) "Add an accent ArticulationEvent to the articulations of `note-event', which is supposed to be a NoteEvent expression." (set! (ly:music-property note-event 'articulations) (cons (make-music 'ArticulationEvent 'articulation-type "accent") (ly:music-property note-event 'articulations))) note-event) @end example Die erste Zeile definiert eine Funktion in Scheme: Die Bezeichnung der Funktion ist @code{add-accent} und sie hat eine Variable mit der Bezeichnung @code{note-event}. In Scheme geht der Typ einer Variable oft direkt aus der Bezeichnung hervor (das ist auch eine gute Methode für andere Programmiersprachen). @example "Add an accent..." @end example @noindent ist eine (englische) Beschreibung, was diese Funktion tut. Sie ist nicht unbedingt notwendig, aber genauso wie klare Variablen-Bezeichnungen ist auch das eine gute Methode. Es kann seltsam scheinen, warum das Notenereignis direkt verändert wird, anstatt mit einer Kopie zu arbeiten (@code{ly:music-deep-copy} kann dafür benützt werden). Der Grund ist eine stille Übereinkunft: musikalische Funktionen dürfen ihre Argumente verändern: sie werden entweder von Grund auf erstellt (wie Eingabe des Benutzers) oder sind schon kopiert (etwa Verweis auf eine Variable mit @samp{\Bezeichnung} oder Noten aus einem Scheme-Ausdruck @samp{$(@dots{})} sind Kopien). Weil es uneffizient wäre, unnötige Kopien zu erstellen, wird der Wiedergabewert einer musikalischen Funktion @emph{nicht} kopiert. Um sich also an die Übereinkunft zu halten, dürfen Argumente nicht mehr als einmal benützt werden, und sie wiederzugeben zählt als eine Benutzung. In einem früheren Beispiel wurden Noten konstruiert, indem ein musikalisches Argument wiederholt wurde. In diesem Fall muss wenigstens eine Wiederholung eine Kopie ihres Arguments sein. Wenn es keine Kopie ist, können seltsame Dinge passieren. Wenn man beispielsweise @code{\relative} oder @code{\transpose} auf die resultierenden Noten anwendet, die die gleichen Elemente mehrmals enthalten, werden die Elemente mehrmals der @code{\relative}-Veränderung oder Transposition unterworfen. Wenn man sie einer musikalischen Variable zuweist, wird dieser Fluch aufgehoben, denn der Verweis auf @samp{\Bezeichnung} erstellt wiederum eine Kopie, die nicht die Identität der wiederholten Elemente überträgt. Während die Funktion oben keine musikalische Funktion ist, wird sie normalerweise inmitten musikalischer Funktionen eingesetzt. Darum ist es sinnvoll, der gleichen Übereinkunft zu folgen, die für musikalische Funktionen gelten: Die Eingabe kann verändert worden sein, um die Ausgabe zu produzieren, und der den Aufruf erstellt, ist verantwortlich für die Erstellung von Kopien, wenn er immernoch die unveränderten Argumente benötigt. Wenn man sich LilyPonds eigene Funktionen wie etwa @code{music-map} anschaut, sieht man, dass sie denselben Prinzipien folgen. Aber wo waren wir? Jetzt gibt es ein @code{note-event}, das verändert werden kann, nicht unter Einsatz von @code{ly:music-deep-copy} sondern aufgrund einer langen Erklärung. Der Akzent wird zu seiner @code{'articulations}-Liste hinzugefügt: @example (set! place neuer-Wert) @end example Was in diesem Fall @qq{gesetzt} werden soll (@qq{place}) ist die @q{'articulations}-Eigenschaft des @code{note-event}-Ausdrucks. @example (ly:music-property note-event 'articulations) @end example @code{ly:music-property} ist die Funktion, mit der musikalische Eigenschaften erreicht werden können (die @code{'articulations}, @code{'duration}, @code{'pitch} usw., die in der Ausgabe von @code{\displayMusic} weiter oben angezeigt werden). Der neue Wert ist, was ehemals die @code{'articulations}-Eigenschaft war, mit einem zusätzlichen Element: dem @code{ArticulationEvent}-Ausdruck, der aus der Ausgabe von @code{\displayMusic} kopiert werden kann: @example (cons (make-music 'ArticulationEvent 'articulation-type "accent") (ly:music-property result-event-chord 'articulations)) @end example @code{cons} wird benutzt, um ein Element vorne an eine Liste hinzuzufügen, ohne dass die originale Liste verändert wird. Das ist es, was die Funktion tun soll: die gleiche Liste wie vorher, aber mit dem neuen @code{ArticulationEvent}-Ausdruck. Die Reihenfolge innerhalb der Elementeeigenschaft ist hier nicht relevant. Wenn schließlich die Akzent-Artikulation zu der entsprechenden @code{elements}-Eigenschaft hinzugefügt ist, kann @code{note-event} ausgegeben werden, darum die letzte Zeile der Funktion. Jetzt wird die @code{add-accent}-Funktion in eine musikalische Funktion umgewandelt (hierzu gehört etwas syntaktischer Zuckerguß und eine Deklaration des Typs ihres einzigen @qq{wirklichen} Arguments: @example addAccent = #(define-music-function (parser location note-event) (ly:music?) "Add an accent ArticulationEvent to the articulations of `note-event', which is supposed to be a NoteEvent expression." (set! (ly:music-property note-event 'articulations) (cons (make-music 'ArticulationEvent 'articulation-type "accent") (ly:music-property note-event 'articulations))) note-event) @end example Eine Überprüfung, dass die Funktion richtig arbeitet, geschieht folgendermaßen: @example \displayMusic \addAccent c4 @end example @ignore @menu * Optimierungen mit Scheme:: @end menu @c node Optimierungen mit Scheme @c appendixsec Optimierungen mit Scheme @c translationof Tweaking with Scheme Wir haben gesehen wie LilyPond-Eingabe massiv beeinflusst werden kann, indem Befehle wie etwa @code{\override TextScript #'extra-offset = ( 1 . -1)} benutzt werden. Aber es wurde gezeigt, dass Scheme noch mächtiger ist. Eine bessere Erklärung findet sich in der@ref{Scheme-Übung} und in @ruser{Schnittstellen für Programmierer}. Scheme kann auch in einfachen @code{\override}-Befehlen benutzt werden: TODO Find a simple example @c This isn't a valid example with skylining @c It works fine without padText -td @lilypond[quote,verbatim,ragged-right] padText = #(define-music-function (parser location padding) (number?) #{ \once \override TextScript #'padding = #padding #}) \relative c''' { c4^"piu mosso" b a b \padText #1.8 c4^"piu mosso" d e f \padText #2.6 c4^"piu mosso" fis a g } @end lilypond Es kann auch benutzt werden, um Befehle zu erstellen: @c Check this is a valid example with skylining @c It is - 'padding still works @lilypond[quote,verbatim,ragged-right] tempoPadded = #(define-music-function (parser location padding tempotext) (number? markup?) #{ \once \override Score.MetronomeMark #'padding = #padding \tempo \markup { \bold #tempotext } #}) \relative c'' { \tempo \markup { "Low tempo" } c4 d e f g1 \tempoPadded #4.0 "High tempo" g4 f e d c1 } @end lilypond Sogar ganze Musikausdrücke können eingefügt werden: @lilypond[quote,verbatim,ragged-right] pattern = #(define-music-function (parser location x y) (ly:music? ly:music?) #{ #x e8 a b $y b a e #}) \relative c''{ \pattern c8 c8\f \pattern {d16 dis} { ais16-> b\p } } @end lilypond @end ignore