[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

1.1 Introduction to Scheme

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.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

1.1.1 Scheme sandbox

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] [ ? ]

1.1.2 Scheme variables

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] [ ? ]

1.1.3 Scheme simple data types

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.

Booleans

Boolean values are True or False. The Scheme for True is #t and False is #f.

Numbers

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

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] [ ? ]

1.1.4 Scheme compound data types

There are also compound data types in Scheme. The types commonly used in LilyPond programming include pairs, lists, alists, and hash tables.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

Pairs

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] [ ? ]

Lists

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] [ ? ]

Association lists (alists)

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] [ ? ]

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 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] [ ? ]

1.1.5 Calculations in Scheme

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] [ ? ]

1.1.6 Scheme procedures

Scheme procedures are executable scheme expressions that return a value resulting from their execution. They can also manipulate variables defined outside of the procedure.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

Defining procedures

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] [ ? ]

Predicates

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] [ ? ]

Return values

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] [ ? ]

1.1.7 Scheme conditionals


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

if

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] [ ? ]

cond

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.

其他语言:deutsch, español, français。
关于 自动语言选择