X-Git-Url: https://git.donarmstrong.com/?a=blobdiff_plain;f=Documentation%2Fextending%2Fscheme-tutorial.itely;h=7503306b1fa56f3e8c16b0f6d25b5f5e8d8f28a6;hb=40aac0ae57ee113faa860ba221d83d9e6312173e;hp=c75f54630001bc8db7b30ec410e067daf0ac9950;hpb=5b1f3adfe5e3561eaa0f9440e0697054084b7107;p=lilypond.git diff --git a/Documentation/extending/scheme-tutorial.itely b/Documentation/extending/scheme-tutorial.itely index c75f546300..7503306b1f 100644 --- a/Documentation/extending/scheme-tutorial.itely +++ b/Documentation/extending/scheme-tutorial.itely @@ -8,7 +8,7 @@ Guide, node Updating translation committishes.. @end ignore -@c \version "2.16.0" +@c \version "2.17.11" @node Scheme tutorial @chapter Scheme tutorial @@ -209,7 +209,15 @@ For a complete listing see the Guile reference guide, There are also compound data types in Scheme. The types commonly used in LilyPond programming include pairs, lists, alists, and hash tables. -@subheading Pairs +@menu +* Pairs:: +* Lists:: +* Association lists (alists):: +* Hash tables:: +@end menu + +@node Pairs +@unnumberedsubsubsec Pairs The foundational compound data type of Scheme is the @code{pair}. As might be expected from its name, a pair is two values glued together. @@ -250,7 +258,7 @@ Scheme procedures @code{car} and @code{cdr}, respectively. @lisp guile> (define mypair (cons 123 "hello there") -... ) +@dots{} ) guile> (car mypair) 123 guile> (cdr mypair) @@ -264,7 +272,8 @@ Note: @code{cdr} is pronounced "could-er", according Sussman and Abelson, see @uref{http://mitpress.mit.edu/sicp/full-text/book/book-Z-H-14.html#footnote_Temp_133} -@subheading Lists +@node Lists +@unnumberedsubsubsec Lists A very common Scheme data structure is the @emph{list}. Formally, a list is defined as either the empty list (represented as @code{'()}, @@ -294,7 +303,8 @@ Lists are a central part of Scheme. In, fact, Scheme is considered a dialect of lisp, where @q{lisp} is an abbreviation for @q{List Processing}. Scheme expressions are all lists. -@subheading Association lists (alists) +@node Association lists (alists) +@unnumberedsubsubsec Association lists (alists) A special type of list is an @emph{association list} or @emph{alist}. An alist is used to store data for easy retrieval. @@ -318,7 +328,8 @@ guile> Alists are widely used in LilyPond to store properties and other data. -@subheading Hash tables +@node Hash tables +@unnumberedsubsubsec Hash tables A data structure that is used occasionally in LilyPond. A hash table is similar to an array, but the indexes to the array can be any type @@ -488,7 +499,14 @@ Scheme procedures are executable scheme expressions that return a value resulting from their execution. They can also manipulate variables defined outside of the procedure. -@subheading Defining procedures +@menu +* Defining procedures:: +* Predicates:: +* Return values:: +@end menu + +@node Defining procedures +@unnumberedsubsubsec Defining procedures Procedures are defined in Scheme with define @@ -514,7 +532,8 @@ guile> (average 3 12) 15/2 @end lisp -@subheading Predicates +@node Predicates +@unnumberedsubsubsec Predicates Scheme procedures that return boolean values are often called @emph{predicates}. By convention (but not necessity), predicate names @@ -528,7 +547,8 @@ guile> (less-than-ten? 15) #f @end lisp -@subheading Return values +@node Return values +@unnumberedsubsubsec Return values Scheme procedures always return a return value, which is the value of the last expression executed in the procedure. The return @@ -554,14 +574,20 @@ statement in the let block: @lisp guile> (let ((x 2) (y 3) (z 4)) (display (+ x y)) (display (- z 4)) -... (+ (* x y) (/ z x))) +@dots{} (+ (* x y) (/ z x))) 508 @end lisp @node Scheme conditionals @subsection Scheme conditionals -@subheading if +@menu +* if:: +* cond:: +@end menu + +@node if +@unnumberedsubsubsec if Scheme has an @code{if} procedure: @@ -581,14 +607,15 @@ 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 +@node cond +@unnumberedsubsubsec cond Another conditional procedure in scheme is @code{cond}: @example (cond (test-expression-1 result-expression-sequence-1) (test-expression-2 result-expression-sequence-2) - ... + @dots{} (test-expression-n result-expression-sequence-n)) @end example @@ -633,7 +660,7 @@ Now LilyPond's input is structured into tokens and expressions, much like human language is structured into words and sentences. LilyPond has a lexer that recognizes tokens (literal numbers, strings, Scheme elements, pitches and so on), and a parser that understands the syntax, -@ruser{LilyPond grammar}. Once it knows that a particular syntax rule +@rcontrib{LilyPond grammar}. Once it knows that a particular syntax rule applies, it executes actions associated with it. The hash mark@tie{}@code{#} method of embedding Scheme is a natural fit @@ -793,7 +820,7 @@ traLaLa = @{ c'4 d'4 @} is internally converted to a Scheme definition: @example -(define traLaLa @var{Scheme value of `@code{... }'}) +(define traLaLa @var{Scheme value of `@code{@dots{}}'}) @end example This means that LilyPond variables and Scheme variables may be freely @@ -835,7 +862,7 @@ Instead of defining @code{\twice}, the example above could also have been written as @example -... +@dots{} $(make-sequential-music newLa) @end example @@ -858,7 +885,7 @@ context. Using those, the last part of the example could have been written as @example -... +@dots{} @{ #@@newLa @} @end example @@ -878,7 +905,7 @@ If you need it to be executed at a later point of time, check out #(define (nopc) (ly:set-option 'point-and-click #f)) -... +@dots{} #(nopc) @{ c'4 @} @end example @@ -902,7 +929,7 @@ the alist with both a key and a value. The LilyPond syntax for doing this is: @example -\override Stem #'thickness = #2.6 +\override Stem.thickness = #2.6 @end example This instruction adjusts the appearance of stems. An alist entry @@ -927,14 +954,23 @@ while @code{twentyFour} is a variable. @node LilyPond compound variables @subsection LilyPond compound variables -@subheading Offsets +@menu +* Offsets:: +* Fractions:: +* Extents:: +* Property alists:: +* Alist chains:: +@end menu + +@node Offsets +@unnumberedsubsubsec Offsets Two-dimensional offsets (X and Y coordinates) are stored as @emph{pairs}. The @code{car} of the offset is the X coordinate, and the @code{cdr} is the Y coordinate. @example -\override TextScript #'extra-offset = #'(1 . 2) +\override TextScript.extra-offset = #'(1 . 2) @end example This assigns the pair @code{(1 . 2)} to the @code{extra-offset} @@ -944,7 +980,8 @@ this command moves the object 1 staff space to the right, and 2 spaces up. Procedures for working with offsets are found in @file{scm/lily-library.scm}. -@subheading Fractions +@node Fractions +@unnumberedsubsubsec Fractions Fractions as used by LilyPond are again stored as @emph{pairs}, this time of unsigned integers. While Scheme can represent rational numbers @@ -954,7 +991,8 @@ no negative @q{fractions} in LilyPond's mind. So @code{2/4} in LilyPond means @code{(2 . 4)} in Scheme, and @code{#2/4} in LilyPond means @code{1/2} in Scheme. -@subheading Extents +@node Extents +@unnumberedsubsubsec Extents Pairs are also used to store intervals, which represent a range of numbers from the minimum (the @code{car}) to the maximum (the @code{cdr}). @@ -967,7 +1005,8 @@ Procedures for working with intervals are found in @file{scm/lily-library.scm}. These procedures should be used when possible to ensure consistency of code. -@subheading Property alists +@node Property alists +@unnumberedsubsubsec Property alists A property alist is a LilyPond data structure that is an alist whose keys are properties and whose values are Scheme expressions that give @@ -975,7 +1014,8 @@ the desired value for the property. LilyPond properties are Scheme symbols, such as @code{'thickness}. -@subheading Alist chains +@node Alist chains +@unnumberedsubsubsec Alist chains An alist chain is a list containing property alists. @@ -1087,30 +1127,37 @@ will display 'text "f")) 'duration - (ly:make-duration 2 0 1 1) + (ly:make-duration 2 0 1/1) 'pitch (ly:make-pitch 0 0 0)))) @end example By default, LilyPond will print these messages to the console along with all the other messages. To split up these messages and save -the results of @code{\display@{STUFF@}}, redirect the output to -a file. +the results of @code{\display@{STUFF@}}, you can specify an optional +output port to use: @example -lilypond file.ly >display.txt +@{ + \displayMusic #(open-output-file "display.txt") @{ c'4\f @} +@} @end example -With a combined bit of Lilypond and Scheme magic, you can actually -let Lilypond direct just this output to a file of its own: - +This will overwrite a previous output file whenever it is called; if you +need to write more than one expression, you would use a variable for +your port and reuse it: @example @{ - #(with-output-to-file "display.txt" - (lambda () #@{ \displayMusic @{ c'4\f @} #@})) + port = #(open-output-file "display.txt") + \displayMusic \port @{ c'4\f @} + \displayMusic \port @{ d'4 @} + #(close-output-port port) @} @end example +Guile's manual describes ports in detail. Closing the port is actually +only necessary if you need to read the file before Lilypond finishes; in +the first example, we did not bother to do so. A bit of reformatting makes the above information easier to read: @@ -1122,16 +1169,16 @@ A bit of reformatting makes the above information easier to read: (make-music 'AbsoluteDynamicEvent 'text "f")) - 'duration (ly:make-duration 2 0 1 1) + 'duration (ly:make-duration 2 0 1/1) 'pitch (ly:make-pitch 0 0 0)))) @end example -A @code{@{ ... @}} music sequence has the name @code{SequentialMusic}, -and its inner expressions are stored as a list in its @code{'elements} -property. A note is represented as a @code{NoteEvent} object (storing -the duration and pitch properties) with attached information (in this -case, an @code{AbsoluteDynamicEvent} with a @code{"f"} text property) -stored in its @code{articulations} property. +A @code{@{ @dots{} @}} music sequence has the name +@code{SequentialMusic}, and its inner expressions are stored as a list +in its @code{'elements} property. A note is represented as a +@code{NoteEvent} object (storing the duration and pitch properties) with +attached information (in this case, an @code{AbsoluteDynamicEvent} with +a @code{"f"} text property) stored in its @code{articulations} property. @funindex{\void} @code{\displayMusic} returns the music it displays, so it will get @@ -1154,7 +1201,7 @@ someNote = c' (make-music 'NoteEvent 'duration - (ly:make-duration 2 0 1 1) + (ly:make-duration 2 0 1/1) 'pitch (ly:make-pitch 0 0 0)) @end example @@ -1172,7 +1219,7 @@ someNote = (list (make-music 'NoteEvent 'duration - (ly:make-duration 2 0 1 1) + (ly:make-duration 2 0 1/1) 'pitch (ly:make-pitch 0 0 0)))) @end example @@ -1190,7 +1237,7 @@ expression. (make-music 'NoteEvent 'duration - (ly:make-duration 2 0 1 1) + (ly:make-duration 2 0 1/1) 'pitch (ly:make-pitch 0 0 0)) @end example @@ -1241,7 +1288,7 @@ representation of the desired result. 'span-direction -1)) 'duration - (ly:make-duration 2 0 1 1) + (ly:make-duration 2 0 1/1) 'pitch (ly:make-pitch 0 5 0)) (make-music @@ -1252,7 +1299,7 @@ representation of the desired result. 'span-direction 1)) 'duration - (ly:make-duration 2 0 1 1) + (ly:make-duration 2 0 1/1) 'pitch (ly:make-pitch 0 5 0)))) @end example @@ -1269,7 +1316,7 @@ Now we examine the input, (make-music 'NoteEvent 'duration - (ly:make-duration 2 0 1 1) + (ly:make-duration 2 0 1/1) 'pitch (ly:make-pitch 0 5 0)))) @end example @@ -1277,7 +1324,7 @@ Now we examine the input, So in our function, we need to clone this expression (so that we have two notes to build the sequence), add a @code{SlurEvent} to the @code{'articulations} property of each one, and finally make a -@code{SequentialMusic} with the two @code{EventChords}. For adding to a +@code{SequentialMusic} with the two @code{NoteEvent} elements. For adding to a property, it is useful to know that an unset property is read out as @code{'()}, the empty list, so no special checks are required before we put another element at the front of the @code{articulations} property. @@ -1301,14 +1348,14 @@ doubleSlur = #(define-music-function (parser location note) (ly:music?) @subsection Adding articulation to notes (example) The easy way to add articulation to notes is to merge two music -expressions into one context, as explained in @ruser{Creating contexts}. +expressions into one context. However, suppose that we want to write a music function that does this. This will have the additional advantage that we can use that music function to add an articulation (like a fingering instruction) to a single note inside of a chord which is not possible if we just merge independent music. -A @code{$variable} inside the @code{#@{...#@}} notation is like +A @code{$variable} inside the @code{#@{@dots{}#@}} notation is like a regular @code{\variable} in classical LilyPond notation. We know that @@ -1335,7 +1382,7 @@ Scheme. We begin by examining our input and desired output, (make-music 'NoteEvent 'duration - (ly:make-duration 2 0 1 1) + (ly:make-duration 2 0 1/1) 'pitch (ly:make-pitch -1 0 0)))) ===== @@ -1350,7 +1397,7 @@ Scheme. We begin by examining our input and desired output, 'articulation-type "accent")) 'duration - (ly:make-duration 2 0 1 1) + (ly:make-duration 2 0 1/1) 'pitch (ly:make-pitch -1 0 0)) @end example @@ -1380,7 +1427,7 @@ from its name. (this is good practice in other programming languages, too!) @example -"Add an accent..." +"Add an accent@dots{}" @end example @noindent @@ -1490,7 +1537,7 @@ We may verify that this music function works correctly, We have seen how LilyPond output can be heavily modified using commands like -@code{\override TextScript #'extra-offset = ( 1 . -1)}. But +@code{\override TextScript.extra-offset = ( 1 . -1)}. But we have even more power if we use Scheme. For a full explanation of this, see the @ref{Scheme tutorial}, and @ref{Interfaces for programmers}. @@ -1506,7 +1553,7 @@ TODO Find a simple example @lilypond[quote,verbatim,ragged-right] padText = #(define-music-function (parser location padding) (number?) #{ - \once \override TextScript #'padding = #padding + \once \override TextScript.padding = #padding #}) \relative c''' { @@ -1530,7 +1577,7 @@ We can use it to create new commands: tempoPadded = #(define-music-function (parser location padding tempotext) (number? markup?) #{ - \once \override Score.MetronomeMark #'padding = #padding + \once \override Score.MetronomeMark.padding = #padding \tempo \markup { \bold #tempotext } #})