X-Git-Url: https://git.donarmstrong.com/?a=blobdiff_plain;f=Documentation%2Fes%2Fextending%2Fscheme-tutorial.itely;h=94b96ec504dfbf5ff208bc785e88ee8e35b94537;hb=75848372d6d640cbc87dc2f431661d401b4a0a26;hp=2234abb2e9356fd4e746a4acba01e633e60a53dd;hpb=c53f805d4ae685312674f2f04e5b6f167600c5ac;p=lilypond.git diff --git a/Documentation/es/extending/scheme-tutorial.itely b/Documentation/es/extending/scheme-tutorial.itely index 2234abb2e9..5905f3e8d9 100644 --- a/Documentation/es/extending/scheme-tutorial.itely +++ b/Documentation/es/extending/scheme-tutorial.itely @@ -1,21 +1,19 @@ @c -*- coding: utf-8; mode: texinfo; documentlanguage: es -*- @ignore - Translation of GIT committish: 5f51567fbc5d7a811e147ebd01f103e066f36b3a + Translation of GIT committish: 3025442087de8dd3f88736290887ced86e360c25 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.12.0" - +@c \version "2.19.22" @node Tutorial de Scheme @appendix Tutorial de Scheme @translationof Scheme tutorial -@funindex # @cindex Scheme @cindex GUILE @cindex Scheme, código en línea @@ -23,48 +21,163 @@ @cindex evaluar Scheme @cindex LISP -LilyPond utiliza el lenguaje de programación Scheme, tanto como parte -de la sintaxis del código de entrada, como para servir de mecanismo -interno que une los módulos del programa entre sí. Esta sección es -una panorámica muy breve sobre cómo introducir datos en Scheme. Si -quiere saber más sobre Scheme, consulte +LilyPond utiliza el lenguaje de programación Scheme, tanto como +parte de la sintaxis del código de entrada, como para servir de +mecanismo interno que une los módulos del programa entre sí. Esta +sección es una panorámica muy breve sobre cómo introducir datos en +Scheme. Si quiere saber más sobre Scheme, consulte @uref{http://@/www@/.schemers@/.org}. LilyPond utiliza la implementación GNU Guile de Scheme, que está basada en el estándar @qq{R5RS} del lenguaje. Si está aprendiendo Scheme para usarlo con LilyPond, no se recomienda trabajar con una -implementación distinta (o que se refiera a un estándar diferente). -Hay información sobre Guile en +implementación distinta (o que se refiera a un estándar +diferente). Hay información sobre Guile en @uref{http://www.gnu.org/software/guile/}. El estándar de Scheme @qq{R5RS} se encuentra en @uref{http://www.schemers.org/Documents/Standards/R5RS/}. -La instalación de LilyPond inclute también la de la implementación -Guile de Scheme. Sobre casi todos los sistemas puede experimentar en -una @qq{caja de arena} de Scheme abriendo una ventana del terminal y -tecleando @q{guile}. En algunos sistemas, sobre todo en Windows, -podría necesitar ajustar la variable de entorno @code{GUILE_LOAD_PATH} -a la carpeta @code{../usr/shr/guile/1.8} dentro de la instalación de -LilyPond (para conocer la ruta completa a esta carpeta, consulte -@ref{Otras fuentes de información}). Como alternativa, los usuarios -de Windows pueden seleccionar simplemente @q{Ejecutar} del menú Inicio -e introducir @q{guile}. - -El concepto más básico de un lenguaje son sus tipos de datos: números, -cadenas de caracteres, listas, etc. He aquí una lista de los tipos de -datos que son de relevancia respecto de la entrada de LilyPond. +@menu +* Introducción a Scheme:: +* Scheme dentro de LilyPond:: +* Construir funciones complicadas:: +@end menu + +@node Introducción a Scheme +@section Introducción a Scheme +@translationof Introduction to Scheme + +Comenzaremos con una introducción a Scheme. Para esta breve +introducción utilizaremos el intérprete GUILE para explorar la +manera en que el lenguaje funciona. Una vez nos hayamos +familiarizado con Scheme, mostraremos cómo se puede integrar el +lenguaje en los archivos de LilyPond. + + +@menu +* Cajón de arena de Scheme:: +* Variables de Scheme:: +* Tipos de datos simples de Scheme:: +* Tipos de datos compuestos de Scheme:: +* Cálculos en Scheme:: +* Procedimientos de Scheme:: +* Condicionales de Scheme:: +@end menu + +@node Cajón de arena de Scheme +@subsection Cajón de arena de Scheme +@translationof Scheme sandbox + +La instalación de LilyPond incluye también la de la implementación +Guile de Scheme. Sobre casi todos los sistemas puede experimentar +en una @qq{caja de arena} de Scheme abriendo una ventana del +terminal y tecleando @q{guile}. En algunos sistemas, sobre todo +en Windows, podría necesitar ajustar la variable de entorno +@code{GUILE_LOAD_PATH} a la carpeta @code{../usr/share/guile/1.8} +dentro de la instalación de LilyPond (para conocer la ruta +completa a esta carpeta, consulte @rlearning{Otras fuentes de +información}). Como alternativa, los usuarios de Windows pueden +seleccionar simplemente @q{Ejecutar} del menú Inicio e introducir +@q{guile}. + +Sin embargo, está disponible un cajón de arena de Scheme listo +para funcionar con todo LilyPond cargado, con esta instrucción de +la línea de órdenes: +@example +lilypond scheme-sandbox +@end example + +@noindent +Una vez está funcionando el cajón de arena, verá un indicador del +sistema de Guile: + +@lisp +guile> +@end lisp + +Podemos introducir expresiones de Scheme en este indicador para +experimentar con Scheme. Si quiere usar la biblioteca readline de +GNU para una más cómoda edición de la línea de órdenes de Scheme, +consulte el archivo @file{ly/scheme-sandbox.ly} para más +información. Si ya ha activado la biblioteca readline para las +sesiones de Guile interactivas fuera de LilyPond, debería +funcionar también en el cajón de arena. + +@node Variables de Scheme +@subsection Variables de Scheme +@translationof Scheme variables + +Las variables de Scheme pueden tener cualquier valor válido de +Scheme, incluso un procedimiento de Scheme. + +Las variables de Scheme se crean con @code{define}: + +@lisp +guile> (define a 2) +guile> +@end lisp + +Las variables de Scheme se pueden evaluar en el indicador del +sistema de guile, simplemente tecleando el nombre de la variable: + +@lisp +guile> a +2 +guile> +@end lisp + +Las variables de Scheme se pueden imprimir en la pantalla +utilizando la función display: + +@lisp +guile> (display a) +2guile> +@end lisp + +@noindent +Observe que el valor @code{2} y el indicador del sistema +@code{guile} se muestran en la misma línea. Esto se puede evitar +llamando al procedimiento de nueva línea o imprimiendo un carácter +de nueva línea. + +@lisp +guile> (display a)(newline) +2 +guile> (display a)(display "\n") +2 +guile> +@end lisp + +Una vez que se ha creado una variable, su valor se puede modificar +con @code{set!}: + +@lisp +guile> (set! a 12345) +guile> a +12345 +guile> +@end lisp + +@node Tipos de datos simples de Scheme +@subsection Tipos de datos simples de Scheme +@translationof Scheme simple data types + +El concepto más básico de un lenguaje son sus tipos de datos: +números, cadenas de caracteres, listas, etc. He aquí una lista de +los tipos de datos que son de relevancia respecto de la entrada de +LilyPond. @table @asis @item Booleanos -Los valores Booleanos son Verdadero y Falso. Verdadero en Scheme es @code{#t} -y Falso es @code{#f}. +Los valores Booleanos son Verdadero y Falso. Verdadero en Scheme es +@code{#t} y Falso es @code{#f}. @funindex ##t @funindex ##f @item Números Los números se escriben de la forma normal, @code{1} es el número -(entero) uno, mientras que @code{-1.5} es un número en coma flotante -(un número no entero). +(entero) uno, mientras que @w{@code{-1.5}} es un número en coma +flotante (un número no entero). @item Cadenas Las cadenas se encierran entre comillas: @@ -81,48 +194,634 @@ es una cadena" @end example -También se pueden añadir comillas y saltos de línea con las llamadas -secuencias de escape. La cadena @code{a dijo "b"} se escribe como +@noindent +y los caracteres de nueva línea al final de cada línea se +incluirán dentro de la cadena. + +Los caracteres de nueva línea también se pueden añadir mediante la +inclusión de @code{\n} en la cadena. + +@example +"esto\nes una\ncadena de varias líneas" +@end example + + +Las comillas dobles y barras invertidas se añaden a las cadenas +precediéndolas de una barra invertida. La cadena @code{\a dijo +"b"} se introduce como @example -"a dijo \"b\"" +"\\a dijo \"b\"" @end example -Los saltos de línea t las barras invertidas se escapan mediante -@code{\n} y @code{\\} respectivamente. @end table +Existen más tipos de datos de Scheme que no se estudian aquí. +Para ver un listado completo, consulte la guía de referencia de +Guile, +@uref{http://www.gnu.org/software/guile/manual/html_node/Simple-Data-Types.html}. + +@node Tipos de datos compuestos de Scheme +@subsection Tipos de datos compuestos de Scheme +@translationof Scheme compound data types + +También existen tipos de datos compuestos en Scheme. Entre los +tipos más usados en la programación de LilyPond se encuentran las +parejas, las listas, las listas-A y las tablas de hash. + +@menu +* Parejas:: +* Listas:: +* Listas asociativas (listas-A):: +* Tablas de hash:: +@end menu + +@node Parejas +@unnumberedsubsubsec Parejas +@translationof Pairs + +El tipo fundacional de datos compuestos de Scheme es la +@code{pareja}. Como se espera por su nombre, una pareja son dos +valores unidos en uno solo. El operador que se usa para formar +una pareja se llama @code{cons}. + +@lisp +guile> (cons 4 5) +(4 . 5) +guile> +@end lisp + +Observe que la pareja se imprime como dos elementos rodeados por +paréntesis y separados por un espacio, un punto (@code{.}) y otro +espacio. El punto @emph{no es} un punto decimal, sino más bien un +indicador de pareja. + +Las parejas también se pueden introducir como valores literales +precediéndolos de un carácter de comilla simple o apóstrofo. + +@lisp +guile> '(4 . 5) +(4 . 5) +guile> +@end lisp + +Los dos elementos de una pareja pueden ser cualquier valor válido +de Scheme: + +@lisp +guile> (cons #t #f) +(#t . #f) +guile> '("bla-bla" . 3.1415926535) +("bla-bla" . 3.1415926535) +guile> +@end lisp + +Se puede accede al primero y segundo elementos de la pareja +mediante los procedimientos de Scheme @code{car} y @code{cdr}, +respectivamente. + +@lisp +guile> (define mipareja (cons 123 "Hola") +@dots{} ) +guile> (car mipareja) +123 +guile> (cdr mipareja) +"Hola" +guile> +@end lisp + +@noindent + +Nota: @code{cdr} se pronuncia "could-er", según Sussman y Abelson, +véase +@uref{http://mitpress.mit.edu/sicp/full-text/book/book-Z-H-14.html#footnote_Temp_133} + +@node Listas +@unnumberedsubsubsec Listas +@translationof Lists + +Una estructura de datos muy común en Scheme es la +@emph{lista}. Formalmente, una lista @q{bien hecha} se define como +la lista vacía, representada como @code{'()} y con longitud cero, +o bien como una pareja cuyo @code{cdr} es a su vez una lista más +corta. + +Existen muchas formas de crear listas. Quizá la más común es con +el procedimiento @code{list}: + +@lisp +guile> (list 1 2 3 "abc" 17.5) +(1 2 3 "abc" 17.5) +@end lisp + +La representación de una lista como elementos individuales +separados por espacios y encerrada entre paréntesis es realmente +una forma compacta de las parejas con punto que constituyen la +lista, donde el punto e inmediatamente un paréntesis de apertura +se suprimen junto al paréntesis de cierre correspondiente. Sin +esta compactación, la salida habría sido +@lisp +(1 . (2 . (3 . ("abc" . (17.5 . ()))))) +@end lisp + +De igual forma que con la salida, una lista puede escribirse +(después de haber añadido un apóstrofo para evitar su +interpretación como una llamada de función) como una lista literal +encerrando sus elementos entre paréntesis: + +@lisp +guile> '(17 23 "fulano" "mengano" "zutano") +(17 23 "fulano" "mengano" "zutano") +@end lisp + +Las listas son una parte fundamental de Scheme. De hecho, Scheme +se considera un dialecto de Lisp, donde @q{lisp} es una +abreviatura de @q{List Processing} (proceso de listas). Todas las +expresiones de Scheme son listas. + + +@node Listas asociativas (listas-A) +@unnumberedsubsubsec Listas asociativas (listas-A) +@translationof Association lists (alists) + +Un tipo especial de listas son las @emph{listas asociativas} o +@emph{listas-A}. Se puede usar una lista-A para almacenar datos +para su fácil recuperación posterior. + +Las listas-A son listas cuyos elementos son parejas. El +@code{car} de cada elemento se llama @emph{clave}, y el @code{cdr} +de cada elemento se llama @emph{valor}. El procedimiento de +Scheme @code{assoc} se usa para recuperar un elemento de la +lista-A, y @code{cdr} se usa para recuperar el valor: + +@lisp +guile> (define mi-lista-a '((1 . "A") (2 . "B") (3 . "C"))) +guile> mi-lista-a +((1 . "A") (2 . "B") (3 . "C")) +guile> (assoc 2 mi-lista-a) +(2 . "B") +guile> (cdr (assoc 2 mi-lista-a)) +"B" +guile> +@end lisp + +Las listas-A se usan mucho en LilyPond para almacenar propiedades +y otros datos. + + +@node Tablas de hash +@unnumberedsubsubsec Tablas de hash +@translationof Hash tables + +Estructuras de datos que se utilizan en LilyPond de forma +ocasional. Una tabla de hash es similar a una matriz, pero los +índices de la matriz pueden ser cualquier tipo de valor de Scheme, +no sólo enteros. + +Las tablas de hash son más eficientes que las listas-A si hay una +gran cantidad de datos que almacenar y los datos cambian con muy +poca frecuencia. + +La sintaxis para crear tablas de hash es un poco compleja, pero +veremos ejemplos de ello en el código fuente de LilyPond. + +@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 + +Los valores se recuperan de las tablas de hash mediante +@code{hashq-ref}. + +@lisp +guile> (hashq-ref h 3) +"val3" +guile> (hashq-ref h 'key2) +"val2" +guile> +@end lisp + +Las claves y los valores se recuperan como una pareja con +@code{hashq-get-handle}. Ésta es la forma preferida, porque +devuelve @code{#f} si no se encuentra la clave. -En un archivo de música, los fragmentos de código de Scheme se -escriben con el signo de almohadilla @code{#}. Así, los ejemplos -anteriores traducidos a LilyPond son: +@lisp +guile> (hashq-get-handle h 'key1) +(key1 . "val1") +guile> (hashq-get-handle h 'frob) +#f +guile> +@end lisp + +@node Cálculos en Scheme +@subsection Cálculos en Scheme +@translationof Calculations in Scheme + +@ignore +Todo el tiempo hemos estado usando listas. Un cálculo, como @code{(+ +1 2)} también es una lista (que contiene el símbolo @code{+} y los +números 1 y@tie{}2). Normalmente, las listas se interpretan como +cálculos, y el intérprete de Scheme sustituye el resultado del +cálculo. Para escribir una lista, detenemos la evaluación. Esto se +hace precediendo la lista por un apóstrofo @code{'}. Así, para los +cálculos no usamos ningún apóstrofo. + +Dentro de una lista o pareja precedida de apóstrofo, no hay necesidad +de escribir ningún apóstrofo más. Lo siguiente es una pareja de +símbolos, una lista de símbolos y una lista de listas respectivamente: @example -##t ##f -#1 #-1.5 -#"esto es una cadena" -#"esto -es -una cadena" +#'(stem . head) +#'(staff clef key-signature) +#'((1) (2)) +@end example +@end ignore + +Scheme se puede usar para hacer cálculos. Utiliza sintaxis +@emph{prefija}. Sumar 1 y@tie{}2 se escribe como @code{(+ 1 2)} y +no como el tradicional @math{1+2}. + +@lisp +guile> (+ 1 2) +3 +@end lisp + +Los cálculos se pueden anidar; el resultado de una función se +puede usar para otro cálculo. + +@lisp +guile> (+ 1 (* 3 4)) +13 +@end lisp + +Estos cálculos son ejemplos de evaluaciones; una expresión como +@code{(* 3 4)} se sustituye por su valor @code{12}. + +Los cálculos de Scheme son sensibles a las diferencias entre +enteros y no enteros. Los cálculos enteros son exactos, mientras +que los no enteros se calculan con los límites de precisión +adecuados: + +@lisp +guile> (/ 7 3) +7/3 +guile> (/ 7.0 3.0) +2.33333333333333 +@end lisp + +Cuando el intérprete de Scheme encuentra una expresión que es una +lista, el primer elemento de la lista se trata como un +procedimiento a evaluar con los argumentos del resto de la lista. +Por tanto, todos los operadores en Scheme son operadores prefijos. + +Si el primer elemento de una expresión de Scheme que es una lista +que se pasa al intérprete @emph{no es} un operador o un +procedimiento, se produce un error: + +@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 + +Aquí podemos ver que el intérprete estaba intentando tratar el 1 +como un operador o procedimiento, y no pudo hacerlo. De aquí que +el error sea "Wrong type to apply: 1". + +Así pues, para crear una lista debemos usar el operador de lista, +o podemos precederla de un apóstrofo para que el intérprete no +trate de evaluarla. + +@lisp +guile> (list 1 2 3) +(1 2 3) +guile> '(1 2 3) +(1 2 3) +guile> +@end lisp + +Esto es un error que puede aparecer cuando trabaje con Scheme +dentro de LilyPond. + +@ignore +La misma asignación se puede hacer también completamente en Scheme, + +@example +#(define veintiCuatro (* 2 doce)) +@end example + +@c this next section is confusing -- need to rewrite + +El @emph{nombre} de una variable también es una expresión, similar a +un número o una cadena. Se introduce como + +@example +#'veintiCuatro +@end example + +@funindex #'symbol +@cindex comillas en Scheme + +El apóstrofo @code{'} evita que el intérprete de Scheme sustituya +@code{veintiCuatro} por @code{24}. En vez de esto, obtenemos el +nombre @code{veintiCuatro}. +@end ignore + + +@node Procedimientos de Scheme +@subsection Procedimientos de Scheme +@translationof Scheme procedures + +Los procedimientos de Scheme son expresiones de Scheme ejecutables +que devuelven un valor resultante de su ejecución. También pueden +manipular variables definidas fuera del procedimiento. + +@menu +* Definir procedimientos:: +* Predicados:: +* Valores de retorno:: +@end menu + +@node Definir procedimientos +@unnumberedsubsubsec Definir procedimientos +@translationof Defining procedures + +Los procedimientos se definen en Scheme con @code{define}: + +@example +(define (nombre-de-la-función arg1 arg2 ... argn) + expresión-de-scheme-que-devuelve-un-valor) +@end example + +Por ejemplo, podemos definir un procedimiento para calcular la +media: + +@lisp +guile> (define (media x y) (/ (+ x y) 2)) +guile> media +# +@end lisp + +Una vez se ha definido un procedimiento, se llama poniendo el +nombre del procedimiento dentro de una lista. Por ejemplo, +podemos calcular la media de 3 y 12: + +@lisp +guile> (media 3 12) +15/2 +@end lisp + + +@node Predicados +@unnumberedsubsubsec Predicados +@translationof Predicates + +Los procedimientos de Scheme que devuelven valores booleanos se +suelen llamar @emph{predicados}. Por convenio (pero no por +necesidad), los nombres de predicados acaban en un signo de +interrogación: + +@lisp +guile> (define (menor-que-diez? x) (< x 10)) +guile> (menor-que-diez? 9) +#t +guile> (menor-que-diez? 15) +#f +@end lisp + + +@node Valores de retorno +@unnumberedsubsubsec Valores de retorno +@translationof Return values + +Los procedimientos de Scheme siempre devuelven un valor de +retorno, que es el valor de la última expresión ejecutada en el +procedimiento. El valor de retorno puede ser cualquier valor de +Scheme válido, incluso una estructura de datos compleja o un +procedimiento. + +A veces, el usuario quiere tener varias expresiones de Scheme +dentro de un procedimiento. Existen dos formas en que se pueden +combinar distintas expresiones. La primera es el procedimiento +@code{begin}, que permite evaluar varias expresiones, y devuelve +el valor de la última expresión. + +@lisp +guile> (begin (+ 1 2) (- 5 8) (* 2 2)) +4 +@end lisp + +La segunda forma de combinar varias expresiones es dentro de un +bloque @code{let}. Dentro de un bloque let, se crean una serie de +ligaduras o asignaciones, y después se evalúa una secuencia de +expresiones que pueden incluir esas ligaduras o asignaciones. El +valor de retorno del bloque let es el valor de retorno de la +última sentencia del bloque let: + +@lisp +guile> (let ((x 2) (y 3) (z 4)) (display (+ x y)) (display (- z 4)) +@dots{} (+ (* x y) (/ z x))) +508 +@end lisp + + +@node Condicionales de Scheme +@subsection Condicionales de Scheme +@translationof Scheme conditionals + +@menu +* if:: +* cond:: +@end menu + +@node if +@unnumberedsubsubsec if +@translationof if + +Scheme tiene un procedimiento @code{if}: + +@example +(if expresión-de-prueba expresión-de-cierto expresión-de-falso) +@end example + +@var{expresión-de-prueba} es una expresión que devuelve un valor +booleano. Si @var{expresión-de-prueba} devuelve @code{#t}, el +procedimiento @code{if} devuelve el valor de la +@var{expresión-de-cierto}, en caso contrario devuelve el valor de +la @var{expresión-de-falso}. + +@lisp +guile> (define a 3) +guile> (define b 5) +guile> (if (> a b) "a es mayor que b" "a no es mayor que b") +"a no es mayor que b" +@end lisp + + +@node cond +@unnumberedsubsubsec cond +@translationof cond + +Otro procedimiento condicional en Scheme es @code{cond}: + +@example +(cond (expresión-de-prueba-1 secuencia-de-expresiones-resultante-1) + (expresión-de-prueba-2 secuencia-de-expresiones-resultante-2) + @dots{} + (expresión-de-prueba-n secuencia-de-expresiones-resultante-n)) +@end example + +Por ejemplo: + +@lisp +guile> (define a 6) +guile> (define b 8) +guile> (cond ((< a b) "a es menor que b") +... ((= a b) "a es igual a b") +... ((> a b) "a es mayor que b")) +"a es menor que b" +@end lisp + +@node Scheme dentro de LilyPond +@section Scheme dentro de LilyPond +@translationof Scheme in LilyPond + +@menu +* Sintaxis del Scheme de LilyPond:: +* Variables de LilyPond:: +* Variables de entrada y Scheme:: +* Importación de Scheme dentro de LilyPond:: +* Propiedades de los objetos:: +* Variables de LilyPond compuestas:: +* Representación interna de la música:: +@end menu + +@node Sintaxis del Scheme de LilyPond +@subsection Sintaxis del Scheme de LilyPond +@translationof LilyPond Scheme syntax +@funindex $ +@funindex # + +El intérprete Guile forma parte de LilyPond, lo que significa que +se puede incluir Scheme dentro de los archivos de entrada de +LilyPond. Existen varios métodos para incluir Scheme dentro de +LilyPond. + +La manera más sencilla es utilizar el símbolo de +almohadilla@tie{}@code{#} antes de una expresión de Scheme. + +Ahora bien, el código de entrada de LilyPond se estructura en +elementos y expresiones, de forma parecida a cómo el lenguaje +humano se estructura en palabras y frases. LilyPond tiene un +analizador léxico que reconoce elementos indivisibles (números +literales, cadenas de texto, elementos de Scheme, nombres de nota, +etc.), y un analizador que entiende la sintaxis, la Gramática de +LilyPond (@rcontrib{LilyPond grammar}). Una vez que sabe que se +aplica una regla sintáctica concreta, ejecuta las acciones +asociadas con ella. + +El método del símbolo de almohadilla@tie{}@code{#} para incrustar +Scheme se adapta de forma natural a este sistema. Una vez que el +analizador léxico ve un símbolo de almohadilla, llama al lector de +Scheme para que lea una expresión de Scheme completa (que puede +ser un identificador, una expresión encerrada entre paréntesis, o +algunas otras cosas). Después de que se ha leído la expresión de +Scheme, se almacena como el valor de un elemento @code{SCM_TOKEN} +de la gramática. Después de que el analizador sintáctico ya sabe +cómo hacer uso de este elemento, llama a Guila para que evalúe la +expresión de Scheme. Dado que el analizador sintáctico suele +requerir un poco de lectura por delante por parte del analizador +léxico para tomar sus decisiones de análisis sintáctico, esta +separación de lectura y evaluación entre los analizadores léxico y +sintáctico es justamente lo que se necesita para mantener +sincronizadas las ejecuciones de expresiones de LilyPond y de +Scheme. Por este motivo se debe usar el símbolo de +almohadilla@tie{}@code{#} para llamar a Scheme siempre que sea +posible. + +Otra forma de llamar al intérprete de Scheme desde LilyPond es el +uso del símbolo de dólar@tie{}@code{$} en lugar de la almohadilla +para introducir las expresiondes de Scheme. En este caso, +LilyPond evalúa el código justo después de que el analizador +léxico lo ha leído. Comprueba el tipo resultante de la expresión +de Scheme y después selecciona un tipo de elemento (uno de los +varios elementos @code{xxx_IDENTIFIER} dentro de la sintaxis) para +él. Crea una @emph{copia} del valor y la usa como valor del +elemento. Si el valor de la expresión es vacío (El valor de Guile +de @code{*unspecified*}), no se pasa nada en absoluto al +analizador sintáctico. + +Éste es, de hecho, el mismo mecanismo exactamente que LilyPond +emplea cuando llamamos a cualquier variable o función musical por +su nombre, como @code{\nombre}, con la única diferencia de que el +nombre viene determinado por el analizador léxico de LilyPond sin +consultar al lector de Scheme, y así solamente se aceptan los +nombres de variable consistentes con el modo actual de LilyPond. + +La acción inmediata de @code{$} puede llevar a alguna que otra +sorpresa, véase @ref{Importación de Scheme dentro de LilyPond}. +La utilización de @code{#} donde el analizador sintáctico lo +contempla es normalmente preferible. Dentro de las expresiones +musicales, aquellas que se crean utilizando @code{#} @emph{se +interprentan} como música. Sin embargo, @emph{no se copian} antes +de ser utilizadas. Si forman parte de alguna estructura que aún +podría tener algún uso, quizá tenga que utilizar explícitamente +@code{ly:music-deep-copy}. + +@funindex $@@ +@funindex #@@ +También existen los operadores de @q{división de listas} +@code{$@@} y @code{#@@} que insertan todos los elementos de una +lista dentro del contexto circundante. + +Ahora echemos un vistazo a algo de código de Scheme real. Los +procedimientos de Scheme se pueden definir dentro de los archivos +de entrada de LilyPond: + +@example +#(define (media a b c) (/ (+ a b c) 3)) @end example -Observe que los comentarios de LilyPond (@code{%} y @code{%@{ %@}}) no -se puedden utilizar dentro del código de Scheme. Los comentarios en -el Scheme de Guile se introducen como sigue: +Observe que los comentarios de LilyPond (@code{%} y @code{%@{ +%@}}) no se pueden utilizar dentro del código de Scheme, ni +siquiera dentro de un archivo de entrada de LilyPond, porque es el +intérprete Guile, y no el analizador léxico de LilyPond, el que +está leyendo la expresión de Scheme. Los comentarios en el Scheme +de Guile se introducen como sigue: @example ; esto es un comentario de una línea #! Esto es un comentario de bloque (no anidable) estilo Guile - Pero se usan rara vez por parte de los Schemers y nunca dentro del - código fuente de LilyPond + Pero se usan rara vez por parte de los Schemers + y nunca dentro del código fuente de LilyPond !# @end example -Se pueden combinar en un mismo archivo de música varias expresiones de -Scheme consecutivas mediante la utilización del operador @code{begin}. -Ello permite que el número de marcas de cuadradillo se redizca a una. +Durante el resto de esta sección, supondremos que los datos se +introducen en un archivo de música, por lo que añadiremos una +almohadilla@tie{}@code{#} al principio de cada una de las +expresiones de Scheme. + +Todas las expresiones de Scheme del nivel jerárquico superior +dentro de un archivo de entrada de LilyPond se pueden combinar en +una sola expresión de Scheme mediante la utilización del operador +@code{begin}: @example #(begin @@ -130,178 +829,911 @@ Ello permite que el número de marcas de cuadradillo se redizca a una. (define menganito 1)) @end example -Si el @code{#} va seguido de un paréntesis de apertura, @code{(}, como -en el ejemplo anterior, el analizador sintáctico permanece dentro del -modo de Scheme hasta que encuentra el paréntesis de cierre -correspondiente, @code{)}, por lo que no son necesarios más símbolos -de @code{#} para introducir una sección de Scheme. -Durante el resto de esta sección, supondremos que los datos se -introducen en un archivo de música, por lo que añadiremos almohadillas -@code{#} en todas partes. +@node Variables de LilyPond +@subsection Variables de LilyPond +@translationof LilyPond variables -Scheme se puede usar para hacer cálculos. Utiliza sintaxis -@emph{prefija}. Sumar 1 y@tie{}2 se escribe como @code{(+ 1 2)} y no -como el tradicional @math{1+2}. +Las variables de LilyPond se almacenan internamente en la forma de +variables de Scheme. Así, -@lisp -#(+ 1 2) - @result{} #3 -@end lisp +@example +doce = 12 +@end example -La flecha @result{} muestra que el resultado de evaluar @code{(+ 1 2)} -es@tie{}@code{3}. Los cálculos se pueden anidar; el resultado de una -función se puede usar para otro cálculo. +@noindent +equivale a -@lisp -#(+ 1 (* 3 4)) - @result{} #(+ 1 12) - @result{} #13 -@end lisp +@example +#(define doce 12) +@end example -Estos cálculos son ejemplos de evaluaciones; una expresión como -@code{(* 3 4)} se sustituye por su valor @code{12}. Algo similar -ocurre con las variables. Después de haber definido una variable +Esto significa que las variables de LilyPond están disponibles +para su uso dentro de expresiones de Scheme. Por ejemplo, +podríamos usar @example -doce = #12 +veintiCuatro = (* 2 doce) @end example @noindent -las variables se pueden usar también dentro de las expresiones, aquí +lo que daría lugar a que el número @emph{24} se almacenase dentro +de la variable @code{veintiCuatro} de LilyPond (y de Scheme). + +El lenguaje Scheme permite la modificación de expresiones +complejas in situ y LilyPond hace uso de esta @q{modificación in +situ} al usar funciones musicales. Pero cuando las expresiones +musicales se almacenan dentro de variables en lugar de ser +introducidas directamente, lo que habitualmente se espera cuando +se pasan a funciones musicales sería que el valor original quedase +intacto. Así pues, cuando se referencia una variable musical con +la barra invertida (como @code{\veintiCuatro}), LilyPond crea una +copia del valor musical de tal variable para utilizarla dentro de +la expresión musical circundante, en lugar de usar el valor de la +variable directamente. + +Por ello, las expresiones musicales de Scheme escritas con la +sintasis de almohadilla @code{#} deberían utilizarse para +cualquier material creado @q{partiendo de cero} (o que se ha +copiado explícitamente) en lugar de utilizarse para referenciar +música directamente. + +@seealso +Manual de extensión: +@ref{Sintaxis del Scheme de LilyPond}. + +@node Variables de entrada y Scheme +@subsection Variables de entrada y Scheme +@translationof Input variables and Scheme + +El formato de entrada contempla la noción de variables: en el +siguiente ejemplo, se asigna una expresión musical a una variable +con el nombre @code{traLaLa}. @example -veintiCuatro = #(* 2 doce) +traLaLa = @{ c'4 d'4 @} @end example @noindent -el número 24 se almacena dentro de la variable @code{veintiCuatro}. -La misma asignación se puede hacer también completamente en Scheme, + +También hay una forma de ámbito: en el ejemplo siguiente, el +bloque @code{\layout} también contiene una variable +@code{traLaLa}, que es independiente de la @code{\traLaLa} +externa. @example -#(define veintiCuatro (* 2 doce)) +traLaLa = @{ c'4 d'4 @} +\layout @{ traLaLa = 1.0 @} @end example -El @emph{nombre} de una variable también es una expresión, similar a -un número o una cadena. Se introduce como +@c +En efecto, cada archivo de entrada constituye un ámbito, y cada +bloque @code{\header}, @code{\midi} y @code{\layout} son ámbitos +anidados dentro del ámbito de nivel superior. + +Tanto las variables como los ámbitos están implementados en el +sistema de módulos de GUILE. A cada ámbito se adjunta un módulo +anónimo de Scheme. Una asignación de la forma: @example -#'veintiCuatro +traLaLa = @{ c'4 d'4 @} @end example -@funindex #'symbol -@cindex comillas en Scheme +@noindent +se convierte internamente en una definición de Scheme: -El apóstrofo @code{'} evita que el intérprete de Scheme sustituya -@code{veintiCuatro} por @code{24}. En vez de esto, obtenemos el -nombre @code{veintiCuatro}. +@example +(define traLaLa @var{Valor Scheme de `@code{@dots{}}'}) +@end example + +Esto significa que las variables de LilyPond y las variables de +Scheme se pueden mezclar con libertad. En el ejemplo siguiente, +se almacena un fragmento de música en la variable @code{traLaLa}, +y se duplica usando Scheme. El resultado se importa dentro de un +bloque @code{\score} por medio de una segunda variable +@code{twice}: + +@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 + +@c Due to parser lookahead + +En realidad, éste es un ejemplo bastante interesante. La +asignación solo tiene lugar después de que el analizador +sintáctico se ha asegurado de que no sigue nada parecido a +@code{\addlyrics}, de manera que necesita comprobar lo que viene a +continuación. Lee el símbolo @code{#} y la expresión de Scheme +siguiente @emph{sin} evaluarla, de forma que puede proceder a la +asignación, y @emph{posteriormente} ejecutar el código de Scheme +sin problema. + +@node Importación de Scheme dentro de LilyPond +@subsection Importación de Scheme dentro de LilyPond +@translationof Importing Scheme in LilyPond +@funindex $ +@funindex # + +El ejemplo anterior muestra cómo @q{exportar} expresiones +musicales desde la entrada al intérprete de Scheme. Lo contrario +también es posible. Colocándolo después de @code{$}, un valor de +Scheme se interpreta como si hubiera sido introducido en la +sintaxis de LilyPond. En lugar de definir @code{\twice}, el +ejemplo anterior podría también haberse escrito como + +@example +@dots{} +$(make-sequential-music newLa) +@end example + +Podemos utilizar @code{$} con una expresión de Scheme en cualquier +lugar en el que usaríamos @code{\@var{nombre}} después de haber +asignado la expresión de Scheme a una variable @var{nombre}. Esta +sustitución se produce dentro del @q{analizador léxico}, de manera +que LilyPond no llega a darse cuenta de la diferencia. + +Sin embargo, existe un inconveniente, el de la medida del tiempo. +Si hubiésemos estado usando @code{$} en vez de @code{#} para +definir @code{newLa} en el ejemplo anterior, la siguiente +definición de Scheme habría fracasado porque @code{traLaLa} no +habría sido definida aún. Para ver una explicación de este +problema de momento temporal, véase @ref{Sintaxis del Scheme de +LilyPond}. + +@funindex $@@ +@funindex #@@ +Un conveniente aspecto posterior pueden ser los operadores de +@q{división de listas} @code{$@@} y @code{#@@} para la inserción +de los elementos de una lista dentro del contexto circundante. +Utilizándolos, la última parte del ejemplo se podría haber escrito +como + +@example +@dots{} +@{ #@@newLa @} +@end example + +Aquí, cada elemento de la lista que está almacenado en +@code{newLa} se toma en secuencia y se inserta en la lista, como +si hubiésemos escrito + +@example +@{ #(first newLa) #(second newLa) @} +@end example + +Ahora bien, en todas esas formas, el código de Scheme se evalúa en +el momento en que el código de entrada aún se está procesando, ya +sea en el analizador léxico o en el analizador sintáctico. Si +necesitamos que se ejecute en un momento posterior, debemos +consultar @ref{Funciones de Scheme vacías}, o almacenarlo dentro +de un procedimiento: + +@example +#(define (nopc) + (ly:set-option 'point-and-click #f)) + +@dots{} +#(nopc) +@{ c'4 @} +@end example -Esta sintaxis se usará con mucha frecuencia, pues muchos de los trucos -de presentación consisten en asignar valores (de Scheme) a variables -internas, por ejemplo +@knownissues + +No es posible mezclar variables de Scheme y de LilyPond con la +opción @option{--safe}. + + +@node Propiedades de los objetos +@subsection Propiedades de los objetos +@translationof Object properties + +Las propiedades de los objetos se almacenan en LilyPond en forma +de cadenas de listas-A, que son listas de listas-A. Las +propiedades se establecen añadiendo valores al principio de la +lista de propiedades. Las propiedades se leen extrayendo valores +de las listas-A. + +El establecimiento de un valor nuevo para una propiedad requiere +la asignación de un valor a la lista-A con una clave y un valor. +La sintaxis de LilyPond para hacer esto es la siguiente: @example -\override Stem #'thickness = #2.6 +\override Stem.thickness = #2.6 @end example -Esta instrucción ajusta el aspecto de las plicas. El valor @code{2.6} -se pone dentro de la variable @code{thickness} de un objeto -@code{Stem}. @code{thickness} se mide a partir del grosor de las -líneas del pentagrama, y así estas plicas serán @code{2.6} veces el -grosor de las líneas del pentagrama. Esto hace que las plicas sean -casi el doble de gruesas de lo normal. Para distinguir entre las -variables que se definen en los archivos de entrada (como -@code{veintiCuatro} en el ejemplo anterior) y las variables de los -objetos internos, llamaremos a las últimas @q{propiedades} y a las -primeras @q{variables.} Así, el objeto plica tiene una propiedad -@code{thickness} (grosor), mientras que @code{veintiCuatro} es una -variable. +Esta instrucción ajusta el aspecto de las plicas. Se añade una +entrada de lista-A @code{'(thickness . 2.6)} a la lista de +propiedades de un objeto @code{Stem}. @code{thickness} se mide a +partir del grosor de las líneas del pentagrama, y así estas plicas +serán @code{2.6} veces el grosor de las líneas del pentagrama. +Esto hace que las plicas sean casi el doble de gruesas de lo +normal. Para distinguir entre las variables que se definen en los +archivos de entrada (como @code{veintiCuatro} en el ejemplo +anterior) y las variables de los objetos internos, llamaremos a +las últimas @q{propiedades} y a las primeras @q{variables.} Así, +el objeto plica tiene una propiedad @code{thickness} (grosor), +mientras que @code{veintiCuatro} es una variable. @cindex propiedades frente a variables @cindex variables frente a propiedades -Los desplazamientos bidimensionales (coordenadas X e Y) así como los -tamaños de los objetos (intervalos con un punto izquierdo y otro -derecho) se introducen como @code{parejas}. Una pareja@footnote{En la -terminología de Scheme, la pareja se llama @code{cons}, y sus dos -elementos se llaman @code{car} y @code{cdr} respectivamente.} se -introduce como @code{(primero . segundo)} y, como los símbolos, se deben -preceder de un apóstrofo: +@c todo -- here we're getting interesting. We're now introducing +@c LilyPond variable types. I think this deserves a section all +@c its own + +@node Variables de LilyPond compuestas +@subsection Variables de LilyPond compuestas +@translationof LilyPond compound variables + +@menu +* Desplazamientos:: +* Fracciones:: +* Dimensiones:: +* Listas-A de propiedades:: +* Cadenas de listas-A:: +@end menu + + +@node Desplazamientos +@unnumberedsubsubsec Desplazamientos +@translationof Offsets + +Los desplazamientos bidimensionales (coordenadas X e Y) se +almacenan como @emph{parejas}. El @code{car} del desplazamiento +es la coordenada X, y el @code{cdr} es la coordenada Y. @example -\override TextScript #'extra-offset = #'(1 . 2) +\override TextScript.extra-offset = #'(1 . 2) @end example -Esto asigna la pareja (1, 2) a la propiedad @code{extra-offset} del -objeto TextScript. Estos números se miden en espacios de pentagrama, -y así esta instrucción mueve el objeto un espacio de pentagrama a la -derecha, y dos espacios hacia arriba. +Esto asigna la pareja @code{(1 . 2)} a la propiedad +@code{extra-offset} del objeto TextScript. Estos números se miden +en espacios de pentagrama, y así esta instrucción mueve el objeto +un espacio de pentagrama a la derecha, y dos espacios hacia +arriba. + +Los procedimientos para trabajar con desplazamientos están en +@file{scm/lily-library.scm}. + +@node Fracciones +@unnumberedsubsubsec Fracciones +@subheading Fractions + +Las fracciones tal y como se utilizan por parte de LilyPond se +almacenan, de nuevo, como @emph{parejas}, esta vez de enteros sin +signo. Mientras que Scheme es capaz de representar números +racionaes como un tipo nativo, musicalmente @samp{2/4} y +@samp{1/2} no son lo mismo, y necesitamos poder distinguir entre +ellos. De igual forma, no existe el concepto de @q{fracciones} +negativas en LilyPond. Así pues, @code{2/4} en LilyPond significa +@code{(2 . 4)} en Scheme, y @code{#2/4} en LilyPond significa +@code{1/2} en Scheme. + + +@node Dimensiones +@unnumberedsubsubsec Dimensiones +@translationof Extents + +Las parejas se usan también para almacenar intervalos, que +representan un rango de números desde el mínimo (el @code{car}) +hasta el máximo (el @code{cdr}). Los intervalos se usan para +almacenar las dimensiones en X y en Y de los objetos imprimibles. +Para dimensiones en X, el @code{car} es la coordenada X de la +parte izquierda, y el @code{cdr} es la coordenada X de la parte +derecha. Para las dimensiones en Y, el @code{car} es la +coordenada inferior, y el @code{cdr} es la coordenada superior. + +Los procedimientos para trabajar con intervalos están en +@file{scm/lily-library.scm}. Se deben usar estos procedimientos +siempre que sea posible, para asegurar la consistencia del código. + + +@node Listas-A de propiedades +@unnumberedsubsubsec Listas-A de propiedades +@translationof Property alists + +Una lista-A de propiedades es una estructura de datos de LilyPond +que es una lista-A cuyas claves son propiedades y cuyos valores +son expresiones de Scheme que dan el valor deseado de la +propiedad. + +Las propiedades de LilyPond son símbolos de Scheme, como por +ejemplo @code{'thickness}. + + +@node Cadenas de listas-A +@unnumberedsubsubsec Cadenas de listas-A +@translationof Alist chains + +Una cadena de listas-A es una lista que contiene listas-A de +propiedades. + +El conjunto de todas las propiedades que se aplican a un grob se +almacena por lo general como una cadena de listas-A. Para poder +encontrar el valor de una propiedad determinada que debería tener +un grob, se busca por todas las listas-A de la cadena, una a una, +tratando de encontrar una entrada que contenga la clave de la +propiedad. Se devuelve la primera entrada de lista-A que se +encuentre, y el valor es el valor de la propiedad. + +El procedimiento de Scheme @code{chain-assoc-get} se usa +normalmente para obtener los valores de propiedades. + +@node Representación interna de la música +@subsection Representación interna de la música +@translationof Internal music representation + +Internamente, la música se representa como una lista de Scheme. +La lista contiene varios elementos que afectan a la salida +impresa. El análisis sintáctico es el proceso de convertir la +música de la representación de entrada de LilyPond a la +representación interna de Scheme. + +Cuando se analiza una expresión musical, se convierte en un +conjunto de objetos musicales de Scheme. La propiedad definitoria +de un objeto musical es que ocupa un tiempo. El tiempo que ocupa +se llama @emph{duración}. Las duraciones se expresan como un +número racional que mide la longitud del objeto musical en +redondas. + +Un objeto musical tiene tres clases de tipos: +@itemize +@item +nombre musical: Cada expresión musical tiene un nombre. Por +ejemplo, una nota lleva a un @rinternals{NoteEvent}, y +@code{\simultaneous} lleva a una @rinternals{SimultaneousMusic}. +Hay una lista de todas las expresiones disponibles en el manual de +Referencia de funcionamiento interno, bajo el epígrafe +@rinternals{Music expressions}. + +@item +@q{type} (tipo) o interface: Cada nombre musical tiene varios +@q{tipos} o interfaces, por ejemplo, una nota es un @code{event}, +pero también es un @code{note-event}, un @code{rhythmic-event}, y +un @code{melodic-event}. Todas las clases de música están +listadas en el manual de Referencia de funcionamiento interno, +bajo el epígrafe @rinternals{Music classes}. + +@item +objeto de C++: Cada objeto musical está representado por un objeto +de la clase @code{Music} de C++. +@end itemize + +La información real de una expresión musical se almacena en +propiedades. Por ejemplo, un @rinternals{NoteEvent} tiene +propiedades @code{pitch} y @code{duration} que almacenan la altura +y la duración de esa nota. Hay una lista de todas la propiedades +disponibles en el manual de Referencia de funcionamiento interno, +bajo el epígrafe @rinternals{Music properties}. + +Una expresión musical compuesta es un objeto musical que contiene +otros objetos musicales dentro de sus propiedades. Se puede +almacenar una lista de objetos dentro de la propiedad +@code{elements} de un objeto musical, o un único objeto musical +@q{hijo} dentro de la propiedad @code{element}. Por ejemplo, +@rinternals{SequentialMusic} tiene su hijo dentro de +@code{elements}, y @rinternals{GraceMusic} tiene su argumento +único dentro de @code{element}. El cuerpo de una repetición se +almacena dentro de la propiedad @code{element} de +@rinternals{RepeatedMusic}, y las alternativas dentro de +@code{elements}. + +@node Construir funciones complicadas +@section Construir funciones complicadas +@translationof Building complicated functions + +Esta sección explica cómo reunir la información necesaria para +crear funciones musicales complicadas. + +@menu +* Presentación de las expresiones musicales:: +* Propiedades musicales:: +* Duplicar una nota con ligaduras (ejemplo):: +* Añadir articulaciones a las notas (ejemplo):: +@end menu + + +@node Presentación de las expresiones musicales +@subsection Presentación de las expresiones musicales +@translationof Displaying music expressions + +@cindex almacenamiento interno +@cindex imprimir expresiones musicales +@cindex representación interna, impresión de +@cindex displayMusic +@funindex \displayMusic -Los dos elementos de una pareja pueden ser valores arbitrarios, por -ejemplo +Si se está escribiendo una función musical, puede ser muy +instructivo examinar cómo se almacena internamente una expresión +musical. Esto se puede hacer con la función musical +@code{\displayMusic}. @example -#'(1 . 2) -#'(#t . #f) -#'("bla-bla" . 3.14159265) +@{ + \displayMusic @{ c'4\f @} +@} @end example -Una lista se escribe encerrando sus elementos entre paréntesis, y -añadiendo un apóstrofo. Por ejemplo, +@noindent +imprime lo siguiente: @example -#'(1 2 3) -#'(1 2 "cadena" #f) +(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 -Todo el tiempo hemos estado usando listas. Un cálculo, como @code{(+ -1 2)} también es una lista (que contiene el símbolo @code{+} y los -números 1 y@tie{}2). Normalmente, las listas se interpretan como -cálculos, y el intérprete de Scheme sustituye el resultado del -cálculo. Para escribir una lista, detenemos la evaluación. Esto se -hace precediendo la lista por un apóstrofo @code{'}. Así, para los -cálculos no usamos ningún apóstrofo. +De forma predeterminada, LilyPond imprime estos mensajes sobre la +consola junto al resto de los mensajes. Para separar estos +mensajes y guardar el resultado de @code{\display@{LOQUESEA@}}, +puede especificar que se use un puerto de salida opcional: -Dentro de una lista o pareja precedida de apóstrofo, no hay necesidad -de escribir ningún apóstrofo más. Lo siguiente es una pareja de -símbolos, una lista de símbolos y una lista de listas respectivamente: +@example +@{ + \displayMusic #(open-output-file "display.txt") @{ c'4\f @} +@} +@end example +Esto sobreescribe el archivo de salida anterior cada vez ques e +llama; si necesitamos escribir más de una expresión, debemos usar +una variable para el puerto y reutilizarla: @example -#'(stem . head) -#'(staff clef key-signature) -#'((1) (2)) +@{ + port = #(open-output-file "display.txt") + \displayMusic \port @{ c'4\f @} + \displayMusic \port @{ d'4 @} + #(close-output-port port) +@} +@end example + +El manual de Guile describe los puertos detalladamente. Solo es +realmente necesario cerrar el puerto si necesitamos leer el +archivo antes de que LilyPond termine; en el primer ejemplo, no +nos hemos molestado en hacerlo. + +Un poco de reformateo hace a la información anterior más fácil de +leer: + +@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 + +Una secuencia musical @code{@{ @dots{} @}} tiene el nombre +@code{SequentialMusic}, y sus expresiones internas se almacenan +coma una lista dentro de su propiedad @code{'elements}. Una nota +se representa como un objeto @code{NoteEvent} (que almacena las +propiedades de duración y altura) con información adjunta (en este +caso, un evento @code{AbsoluteDynamicEvent} con una propiedad +@code{"f"} de texto) almacenada en su propiedad +@code{articulations}. + +@funindex{\void} + +@code{\displayMusic} devuelve la música que imprime en la consola, +y por ello se interpretará al tiempo que se imprime en la consola. +Para evitar la interpretación, escriba @code{\void} antes de +@code{\displayMusic}. + +@node Propiedades musicales +@subsection Propiedades musicales +@translationof Music properties + +@ignore +TODO -- make sure we delineate between @emph{music} properties, +@emph{context} properties, and @emph{layout} properties. These +are potentially confusing. +@end ignore + +Veamos un ejemplo: + +@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 + +El objeto @code{NoteEvent} es la representación de +@code{someNote}. Sencillo. ¿Y si ponemos el c' dentro de un +acorde? + +@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 + +Ahora el objeto @code{NoteEvent} es el primer objeto de la +propiedad @code{'elements} de @code{someNote}. + +La función @code{display-scheme-music} es la función que se usa +por parte de @code{\displayMusic} para imprimir la representación +de Scheme de una expresión musical. + +@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 + +Después se accede a la altura de la nota a través de la propiedad +@code{'pitch} del objeto @code{NoteEvent}: + +@example +#(display-scheme-music + (ly:music-property (first (ly:music-property someNote 'elements)) + 'pitch)) +===> +(ly:make-pitch 0 0 0) +@end example + +La altura de la nota se puede cambiar estableciendo el valor de +esta propiedad @code{'pitch}. + +@funindex \displayLilyMusic + +@example +#(set! (ly:music-property (first (ly:music-property someNote 'elements)) + 'pitch) + (ly:make-pitch 0 1 0)) ;; establecer la altura a d'. +\displayLilyMusic \someNote +===> +d'4 +@end example + + +@node Duplicar una nota con ligaduras (ejemplo) +@subsection Duplicar una nota con ligaduras (ejemplo) +@translationof Doubling a note with slurs (example) + +Supongamos que queremos crear una función que convierte una +entrada como @code{a} en @code{@{ a( a) @}}. Comenzamos +examinando la representación interna de la música con la que +queremos terminar. + +@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 + +La mala noticia es que las expresiones @code{SlurEvent} se deben +añadir @q{dentro} de la nota (dentro de la propiedad +@code{articulations}). + +Ahora examinamos la entrada. + +@example +\displayMusic a' +===> +(make-music + 'NoteEvent + 'duration + (ly:make-duration 2 0 1/1) + 'pitch + (ly:make-pitch 0 5 0)))) +@end example + +Así pues, en nuestra función, tenemos que clonar esta expresión +(de forma que tengamos dos notas para construir la secuencia), +añadir @code{SlurEvent} a la propiedad @code{'articulations} de +cada una de ellas, y por último hacer una secuencia +@code{SequentialMusic} con los dos elementos @code{NoteEvent}. +Para añadir a una propiedad, es útil saber que una propiedad no +establecida se lee como @code{'()}, la lista vacía, así que no se +requiere ninguna comprobación especial antes de que pongamos otro +elemento delante de la propiedad @code{articulations}. + + +@example +doubleSlur = #(define-music-function (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 Añadir articulaciones a las notas (ejemplo) +@subsection Añadir articulaciones a las notas (ejemplo) +@translationof Adding articulation to notes (example) + +La manera fácil de añadir articulación a las notas es mezclar dos +expresiones musicales en un solo contexto. Sin embargo, +supongamos que queremos escribir una función musical que lo haga. +Esto tiene la ventaja adicional de que podemos usar esa función +musical para añadir una articulación (como una instrucción de +digitación) a una nota única dentro de un acorde, lo cual no es +posible si nos limitamos a mezclar fragmentos de música +independientes. + +Una @code{$variable} dentro de la notación @code{#@{@dots{}#@}} es +como una @code{\variable} normal en la notación clásica de +LilyPond. Sabemos que + +@example +@{ \music -. -> @} +@end example + +@noindent +no funciona en LilyPond. Podríamos evitar este problema +adjuntando la articulación a un acorde vacío, + +@example +@{ << \music <> -. -> >> @} +@end example + +@noindent +pero a los efectos de este ejemplo, aprenderemos ahora cómo +hacerlo en Scheme. Empezamos examinando nuestra entrada y la +salida deseada. + +@example +% input +\displayMusic c4 +===> +(make-music + 'NoteEvent + 'duration + (ly:make-duration 2 0 1/1) + 'pitch + (ly:make-pitch -1 0 0)))) +===== +% desired output +\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)) +@end example + +Vemos que una nota (@code{c4}) se representa como una expresión +@code{NoteEvent}. Para añadir una articulación de acento, se debe +añadir una expresión @code{ArticulationEvent} a la propiedad +@code{articulations} de la expresión @code{NoteEvent}. + +Para construir esta función, empezamos con + +@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 + +La primera línea es la forma de definir una función en Scheme: el +nombre de la función es @code{add-accent}, y tiene una variable +llamada @code{note-event}. En Scheme, el tipo de variable suele +quedar claro a partir de su nombre (¡esto también es una buena +práctica en otros lenguajes de programación!) + +@example +"Add an accent@dots{}" +@end example + +@noindent +es una descripción de lo que hace la función. No es estrictamente +necesaria, pero de igual forma que los nombres claros de variable, +es una buena práctica. + +Se preguntará por qué modificamos el evento de nota directamente +en lugar de trabajar sobre una copia (se puede usar +@code{ly:music-deep-copy} para ello). La razón es un contrato +silencioso: se permite que las funciones musicales modifiquen sus +argumentos; o bien se generan partiendo de cero (como la entrada +del usuario) o están ya copiadas (referenciar una variable de +música con @samp{\name} o la música procedente de expresiones de +Scheme inmediatas @samp{$(@dots{})} proporcionan una copia). Dado +que sería ineficiente crear copias innecesarias, el valor devuelto +de una función musical @emph{no} se copia. Así pues, para cumplir +dicho contrato, no debemos usar ningún argumento más de una vez, y +devolverlo cuenta como una vez. + +En un ejemplo anterior, hemos construido música mediante la +repetición de un argumento musical dado. En tal caso, al menos +una repetidión tuvo que ser una copia de sí misma. Si no lo +fuese, podrían ocurrir cosas muy extrañas. Por ejemplo, si usamos +@code{\relative} o @code{\transpose} sobre la música resultante +que contiene los mismos elementos varias veces, estarían sujetos +varias veces a la relativización o al transporte. Si los +asignamos a una variable de música, se rompe el curso porque hacer +referencia a @samp{\name} creará de nuevo una copia que no retiene +la identidad de los elementos repetidos. + +Ahora bien, aun cuando la función anterior no es una función +musical, se usará normalmente dentro de funciones musicales. Así +pues, tiene sentido obedecer el mismo convenio que usamos para las +funciones musicales: la entrada puede modificarse para producir la +salida, y el código que llama es responsable de crear las copias +si aún necesita el propio argumento sin modificar. Si observamos +las propias funciones de LilyPond como @code{music-map}, veremos +que se atienen a los mismos principios. + +¿En qué punto nos encontramos? Ahora tenemos un @code{note-event} +que podemos modificar, no a causa de la utilización de +@code{ly:music-deep-copy} sino por una explicación muy +desarrollada. Añadimos el acento a su propiedad de lista +@code{'articulations}. + +@example +(set! place new-value) +@end example + +Aquí, lo que queremos establecer (el @q{place}) es la propiedad +@code{'articulations} de la expresión @code{note-event}. + +@example +(ly:music-property note-event 'articulations) +@end example + +@code{ly:music-property} es la función ustilizada para acceder a las +propiedades musicales (las @code{'articulations}, @code{'duration}, +@code{'pitch}, etc, que vemos arriba en la salida de +@code{\displayMusic}). El nuevo valor es la antigua propiedad +@code{'articulations}, con un elemento adicional: la expresión +@code{ArticulationEvent}, que copiamos a partir de la salida de +@code{\displayMusic}, + +@example +(cons (make-music 'ArticulationEvent + 'articulation-type "accent") + (ly:music-property result-event-chord 'articulations)) +@end example + +Se usa @code{cons} para añadir un elemento a la parte delantera de +una lista sin modificar la lista original. Esto es lo que +queremos: la misma lista de antes, más la nueva expresión +@code{ArticulationEvent}. El orden dentro de la propiedad +@code{'articulations} no tiene importancia aquí. + +Finalmente, una vez hemos añadido la articulación de acento a su +propiedad @code{articulations}, podemos devolver +@code{note-event}, de aquí la última línea de la función. + +Ahora transformamos la función @code{add-accent} en una función +musical (es cuestión de un poco de aderezo sintáctico y una +declaración del tipo de su argumento). + +@example +addAccent = #(define-music-function (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 + +A continuación verificamos que esta función musical funciona +correctamente: + +@example +\displayMusic \addAccent c4 +@end example + + +@ignore @menu * Trucos con Scheme:: @end menu -@node Trucos con Scheme -@appendixsec Trucos con Scheme -@translationof Tweaking with Scheme +@c @nod e Trucos con Scheme +@c @sectio n Trucos con Scheme +@c @transl ationof Tweaking with Scheme Hemos visto cómo la salida de LilyPond se puede modificar -profundamente usando instrucciones como @code{\override TextScript -#'extra-offset = ( 1 . -1)}. Pero tenemos incluso mucho más poder si +profundamente usando instrucciones como +@code{\override TextScript.extra-offset = ( 1 . -1)}. +Pero tenemos incluso mucho más poder si utilizamos Scheme. Para ver una explicación completa de esto, consulte el @ref{Tutorial de Scheme}, y @ruser{Interfaces para programadores}. Podemos usar Scheme simplemente para sobreescribir instrucciones con @code{\override}, +TODO Find a simple example @c This isn't a valid example with skylining @c It works fine without padText -td +@end ignore @ignore @lilypond[quote,verbatim,ragged-right] -padText = #(define-music-function (parser location padding) (number?) +padText = #(define-music-function (padding) (number?) #{ - \once \override TextScript #'padding = #$padding + \once \override TextScript.padding = #padding #}) -\relative c''' { - c4^"piu mosso" b a b +\relative { + c'''4^"piu mosso" b a b \padText #1.8 c4^"piu mosso" d e f \padText #2.6 @@ -310,33 +1742,36 @@ padText = #(define-music-function (parser location padding) (number?) @end lilypond @end ignore +@ignore Lo podemos usar para crear instrucciones nuevas: @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? string?) +tempoPadded = #(define-music-function (padding tempotext) + (number? markup?) #{ - \once \override Score.MetronomeMark #'padding = $padding - \tempo \markup { \bold $tempotext } + \once \override Score.MetronomeMark.padding = #padding + \tempo \markup { \bold #tempotext } #}) -\relative c'' { +\relative { \tempo \markup { "Low tempo" } - c4 d e f g1 - \tempoPadded #4.0 #"High tempo" + c''4 d e f g1 + \tempoPadded #4.0 "High tempo" g4 f e d c1 } @end lilypond + Incluso se le pueden pasar expresiones musicales: @lilypond[quote,verbatim,ragged-right] -pattern = #(define-music-function (parser location x y) (ly:music? ly:music?) +pattern = #(define-music-function (x y) (ly:music? ly:music?) #{ - $x e8 a b $y b a e + #x e8 a b #y b a e #}) \relative c''{ @@ -344,4 +1779,4 @@ pattern = #(define-music-function (parser location x y) (ly:music? ly:music?) \pattern {d16 dis} { ais16-> b\p } } @end lilypond - +@end ignore