| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
We begin with an introduction to Scheme. For this brief introduction, we will use the GUILE interpreter to explore how the language works. Once we are familiar with Scheme, we will show how the language can be integrated in LilyPond files.
| 1.1.1 Scheme sandbox | ||
| 1.1.2 Scheme variables | ||
| 1.1.3 Scheme simple data types | ||
| 1.1.4 Scheme compound data types | ||
| 1.1.5 Calculations in Scheme | ||
| 1.1.6 Scheme procedures | ||
| 1.1.7 Scheme conditionals |
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
The LilyPond installation includes the Guile implementation of
Scheme. On most systems you can experiment in a Scheme sandbox by
opening a terminal window and typing ‘guile’. On some systems,
notably Windows, you may need to set the environment variable
GUILE_LOAD_PATH to the directory ../usr/share/guile/1.8
in the LilyPond installation. For the full path to this directory
see
(learning)Other sources of information section ‘Other sources of information’ in Learning Manual. Alternatively, Windows
users may simply choose ‘Run’ from the Start menu and enter
‘guile’.
However, a hands-on Scheme sandbox with all of LilyPond loaded is available with this command line:
lilypond scheme-sandbox |
Once the sandbox is running, you will receive a guile prompt:
guile> |
You can enter Scheme expressions at this prompt to experiment with Scheme. If you want to be able to use the GNU readline library for nicer editing of the Scheme command line, check the file ‘ly/scheme-sandbox.ly’ for more information. If you already have enabled the readline library for your interactive Guile sessions outside of LilyPond, this should work in the sandbox as well.
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
Scheme variables can have any valid scheme value, including a Scheme procedure.
Scheme variables are created with define:
guile> (define a 2) guile> |
Scheme variables can be evaluated at the guile prompt simply by typing the variable name:
guile> a 2 guile> |
Scheme variables can be printed on the display by using the display function:
guile> (display a) 2guile> |
Note that both the value 2 and the guile prompt guile
showed up on the same line. This can be avoided by calling the
newline procedure or displaying a newline character.
guile> (display a)(newline) 2 guile> (display a)(display "\n") 2 guile> |
Once a variable has been created, its value can be changed with set!:
guile> (set! a 12345) guile> a 12345 guile> |
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
The most basic concept in a language is data typing: numbers, character strings, lists, etc. Here is a list of simple Scheme data types that are often used with LilyPond.
Boolean values are True or False. The Scheme for True is #t
and False is #f.
Numbers are entered in the standard fashion,
1 is the (integer) number one, while -1.5 is a
floating point number (a non-integer number).
Strings are enclosed in double quotes:
"this is a string" |
Strings may span several lines:
"this is a string" |
and the newline characters at the end of each line will be included in the string.
Newline characters can also be added by including \n in the
string.
"this\nis a\nmultiline string" |
Quotation marks and backslashes are added to strings
by preceding them with a backslash.
The string \a said "b" is entered as
"\\a said \"b\"" |
There are additional Scheme data types that are not discussed here. For a complete listing see the Guile reference guide, http://www.gnu.org/software/guile/docs/docs-1.8/guile-ref/Simple-Data-Types.html.
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
There are also compound data types in Scheme. The types commonly used in LilyPond programming include pairs, lists, alists, and hash tables.
| Pairs | ||
| Lists | ||
| Association lists (alists) | ||
| Hash tables |
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
The foundational compound data type of Scheme is the pair. As
might be expected from its name, a pair is two values glued together.
The operator used to form a pair is called cons.
guile> (cons 4 5) (4 . 5) guile> |
Note that the pair is displayed as two items surrounded by
parentheses and separated by whitespace, a period (.), and
more whitespace. The period is not a decimal point, but
rather an indicator of the pair.
Pairs can also be entered as literal values by preceding them with a single quote character.
guile> '(4 . 5) (4 . 5) guile> |
The two elements of a pair may be any valid Scheme value:
guile> (cons #t #f)
(#t . #f)
guile> '("blah-blah" . 3.1415926535)
("blah-blah" . 3.1415926535)
guile>
|
The first and second elements of the pair can be accessed by the
Scheme procedures car and cdr, respectively.
guile> (define mypair (cons 123 "hello there") … ) guile> (car mypair) 123 guile> (cdr mypair) "hello there" guile> |
Note: cdr is pronounced "could-er", according Sussman and
Abelson, see
http://mitpress.mit.edu/sites/default/files/sicp/full-text/book/book-Z-H-14.html#footnote_Temp_133
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
A very common Scheme data structure is the list. Formally,
a ‘proper’ list is defined to be either the empty list with its
input form '() and length 0, or a pair whose
cdr in turn is a shorter list.
There are many ways of creating lists. Perhaps the most common is
with the list procedure:
guile> (list 1 2 3 "abc" 17.5) (1 2 3 "abc" 17.5) |
Representing a list as individual elements separated by whitespace and enclosed in parentheses is actually a compacted rendition of the actual dotted pairs constituting the list, where the dot and an immediately following starting paren are removed along with the matching closing paren. Without this compaction, the output would have been
(1 . (2 . (3 . ("abc" . (17.5 . ())))))
|
As with the output, a list can also be entered (after adding a quote to avoid interpretation as a function call) as a literal list by enclosing its elements in parentheses:
guile> '(17 23 "foo" "bar" "bazzle") (17 23 "foo" "bar" "bazzle") |
Lists are a central part of Scheme. In, fact, Scheme is considered a dialect of lisp, where ‘lisp’ is an abbreviation for ‘List Processing’. Scheme expressions are all lists.
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
A special type of list is an association list or alist. An alist is used to store data for easy retrieval.
Alists are lists whose elements are pairs. The car of each
element is called the key, and the cdr of each element
is called the value. The Scheme procedure assoc is
used to retrieve an entry from the alist, and cdr is used to
retrieve the value:
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> |
Alists are widely used in LilyPond to store properties and other data.
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
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 of Scheme value, not just integers.
Hash tables are more efficient than alists if there is a lot of data to store and the data changes very infrequently.
The syntax to create hash tables is a bit complex, but you can see examples of it in the LilyPond source.
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" |
Values are retrieved from hash tables with hashq-ref.
guile> (hashq-ref h 3) "val3" guile> (hashq-ref h 'key2) "val2" guile> |
Keys and values are retrieved as a pair with hashq-get-handle.
This is a preferred way, because it will return #f if a key is
not found.
guile> (hashq-get-handle h 'key1) (key1 . "val1") guile> (hashq-get-handle h 'frob) #f guile> |
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
Scheme can be used to do calculations. It uses prefix
syntax. Adding 1 and 2 is written as (+ 1 2) rather than the
traditional 1+2.
guile> (+ 1 2) 3 |
Calculations may be nested; the result of a function may be used for another calculation.
guile> (+ 1 (* 3 4)) 13 |
These calculations are examples of evaluations; an expression like
(* 3 4) is replaced by its value 12.
Scheme calculations are sensitive to the differences between integers and non-integers. Integer calculations are exact, while non-integers are calculated to the appropriate limits of precision:
guile> (/ 7 3) 7/3 guile> (/ 7.0 3.0) 2.33333333333333 |
When the scheme interpreter encounters an expression that is a list, the first element of the list is treated as a procedure to be evaluated with the arguments of the remainder of the list. Therefore, all operators in Scheme are prefix operators.
If the first element of a Scheme expression that is a list passed to the interpreter is not an operator or procedure, an error will occur:
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> |
Here you can see that the interpreter was trying to treat 1 as an operator or procedure, and it couldn’t. Hence the error is "Wrong type to apply: 1".
Therefore, to create a list we need to use the list operator, or to quote the list so that the interpreter will not try to evaluate it.
guile> (list 1 2 3) (1 2 3) guile> '(1 2 3) (1 2 3) guile> |
This is an error that can appear as you are working with Scheme in LilyPond.
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
Scheme procedures are executable scheme expressions that return a value resulting from their execution. They can also manipulate variables defined outside of the procedure.
| Defining procedures | ||
| Predicates | ||
| Return values |
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
Procedures are defined in Scheme with define
(define (function-name arg1 arg2 ... argn) scheme-expression-that-gives-a-return-value) |
For example, we could define a procedure to calculate the average:
guile> (define (average x y) (/ (+ x y) 2)) guile> average #<procedure average (x y)> |
Once a procedure is defined, it is called by putting the procedure name and the arguments in a list. For example, we can calculate the average of 3 and 12:
guile> (average 3 12) 15/2 |
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
Scheme procedures that return boolean values are often called predicates. By convention (but not necessity), predicate names typically end in a question mark:
guile> (define (less-than-ten? x) (< x 10)) guile> (less-than-ten? 9) #t guile> (less-than-ten? 15) #f |
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
Scheme procedures always return a return value, which is the value of the last expression executed in the procedure. The return value can be any valid Scheme value, including a complex data structure or a procedure.
Sometimes the user would like to have multiple Scheme expressions in
a procedure. There are two ways that multiple expressions can be
combined. The first is the begin procedure, which allows
multiple expressions to be evaluated, and returns the value of
the last expression.
guile> (begin (+ 1 2) (- 5 8) (* 2 2)) 4 |
The second way to combine multiple expressions is in a let block.
In a let block, a series of bindings are created, and then a sequence
of expressions that can include those bindings is evaluated. The
return value of the let block is the return value of the last
statement in the let block:
guile> (let ((x 2) (y 3) (z 4)) (display (+ x y)) (display (- z 4)) … (+ (* x y) (/ z x))) 508 |
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
| if | ||
| cond |
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
Scheme has an if procedure:
(if test-expression true-expression false-expression) |
test-expression is an expression that returns a boolean
value. If test-expression returns #t, the if
procedure returns the value of true-expression, otherwise
it returns the value of 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" |
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
Another conditional procedure in scheme is cond:
(cond (test-expression-1 result-expression-sequence-1)
(test-expression-2 result-expression-sequence-2)
…
(test-expression-n result-expression-sequence-n))
|
For example:
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" |
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] |
This document was generated by gub on July 13, 2020 using texi2html 1.82.
Altres idiomes: deutsch, español, français.
Quant a la selecció automàtica de la llengua.