X-Git-Url: https://git.donarmstrong.com/?a=blobdiff_plain;f=Documentation%2Fes%2Fextending%2Fscheme-tutorial.itely;h=73d867b5f6119e68930cb2fe8807f405b345912e;hb=4ae6d916b66a89f4fd4699224624c4e9cc996a2b;hp=daed4146e603a7ef67522bea8869bb928bf8dba9;hpb=7eb9c626943a47141ec2cc5fad7723f69e04bbd2;p=lilypond.git diff --git a/Documentation/es/extending/scheme-tutorial.itely b/Documentation/es/extending/scheme-tutorial.itely index daed4146e6..73d867b5f6 100644 --- a/Documentation/es/extending/scheme-tutorial.itely +++ b/Documentation/es/extending/scheme-tutorial.itely @@ -1,20 +1,19 @@ @c -*- coding: utf-8; mode: texinfo; documentlanguage: es -*- @ignore - Translation of GIT committish: 10bd5cc93870ac4b884b8cb938cfc6a19c768097 + Translation of GIT committish: 1551dd6bf65c0236b9620ee966599f5d811d77d8 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.13.36" +@c \version "2.15.20" @node Tutorial de Scheme @appendix Tutorial de Scheme @translationof Scheme tutorial -@funindex # @cindex Scheme @cindex GUILE @cindex Scheme, código en línea @@ -74,13 +73,21 @@ 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 +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}. -Una vez está funcionando el cajón de arena de Guile, verá un indicador +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 @@ -88,7 +95,12 @@ guile> @end lisp Podemos introducir expresiones de Scheme en este indicador para -experimentar con Scheme. +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 @@ -161,7 +173,7 @@ Los valores Booleanos son Verdadero y Falso. Verdadero en Scheme es @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 +(entero) uno, mientras que @w{@code{-1.5}} es un número en coma flotante (un número no entero). @item Cadenas @@ -639,30 +651,80 @@ guile> (cond ((< a b) "a es menor que b") @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. -La marca de almohadilla @code{#} se utiliza para indicar al analizador -sintáctico de LilyPond que lo siguiente es un valor de Scheme. - -Una vez el analizador sintáctico se encuentra con un símbolo de -almohadilla, la entrada se le pasa al intérprete Guile para evaluar la -expresión de Scheme. El intérprete continúa procesando la entrada -hasta que se encuentra con el final de una expresión de Scheme. - -Los procedimientos de Scheme se pueden definir 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 +sintáctico que entiende la sintaxis, la @ruser{Gramática de LilyPond}. +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 su final 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{Variables de entrada y Scheme}. La utilización +de @code{#} donde el analizador sintáctico lo contempla es normalmente +preferible. + +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 (average a b c) (/ (+ a b c) 3)) +#(define (media a b c) (/ (+ a b c) 3)) @end example 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 input file, porque es el intérprete -Guile, y no el analizador sintáctico de LilyPond, el que está -interpretando la expresión de Scheme. Los comentarios en el Scheme de -Guile se introducen como sigue: +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 @@ -675,8 +737,8 @@ Guile se introducen como sigue: @end example 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{#} al principio de todas las expresiones de Scheme. +introducen en un archivo de música, por lo que añadiremos +almohadillas@tie{}@code{#} al principio de todas 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 @@ -718,6 +780,18 @@ veintiCuatro = (* 2 doce) lo que daría lugar a que el número 24 se almacenase dentro de la variable @code{veintiCuatro} de LilyPond (y de Scheme). +La forma usual de referirse a las variables de LilyPond, +@ref{Sintaxis del Scheme de LilyPond}, + +es llamarlas usando una barra invertida, es decir +@code{\veintiCuatro}. Dado que esto crea una copia para la mayor +parte de los tipos internos de LilyPond, concretamente las expresiones +musicales, las funciones musicales no sueln crear copias del material +que ellas mismas modifican. Por este motivo, las expresiones +musicales dadas con @code{#} no deberían, por lo general, contener +material que no se haya creado partiendo de cero o copiado +explícitamente en lugar de estar referenciado directamente. + @node Variables de entrada y Scheme @subsection Variables de entrada y Scheme @translationof Input variables and Scheme @@ -770,9 +844,6 @@ usando Scheme. El resultado se importa dentro de un bloque @lilypond[verbatim] traLaLa = { c'4 d'4 } -%% dummy action to deal with parser lookahead -#(display "this needs to be here, sorry!") - #(define newLa (map ly:music-deep-copy (list traLaLa traLaLa))) #(define twice @@ -783,27 +854,44 @@ traLaLa = { c'4 d'4 } @c Due to parser lookahead -En este ejemplo, la asignación se produce después de que el analizador -sintáctico ha verificado que no ocurre nada interesante después de -@code{traLaLa = @{ ... @}}. Sin la sentencia muda del ejemplo -anterior, la definición de @code{newLa} se ejecuta antes de que se -defina @code{traLaLa}, produciendo un error de sintaxis. +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. El ejemplo anterior muestra cómo @q{exportar} expresiones musicales desde la entrada al intérprete de Scheme. Lo contrario también es -posible. Envolviendo un valor de Scheme en la función -@code{ly:export}, se interpreta un valor de Scheme 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 +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 ... -@{ #(ly:export (make-sequential-music (list newLa))) @} +@{ $(make-sequential-music (list newLa)) @} @end example -El código de Scheme se evalúa tan pronto como el analizador sintáctico -lo encuentra. Para definir código de Scheme dentro de un macro (para -llamarse más tarde), utilice @ref{Funciones vacías}, o bien +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}. + +En cualquier caso, la evaluación del código de Scheme se produce +dentro del analizador sintáctico como muy tarde. Si necesitamos que +se ejecute en un punto temporal más tardío, +usaríamos @ref{Funciones de Scheme vacías}, o lo almacenaríamos en un +macro: @example #(define (nopc) @@ -817,7 +905,7 @@ llamarse más tarde), utilice @ref{Funciones vacías}, o bien @knownissues No es posible mezclar variables de Scheme y de LilyPond con la opción -@code{--safe}. +@option{--safe}. @node Propiedades de los objetos @@ -864,7 +952,7 @@ tiene una propiedad @code{thickness} (grosor), mientras que @subheading Desplazamientos Los desplazamientos bidimensionales (coordenadas X e Y) se almacenan -como @code{parejas}. El @code{car} del desplazamiento es la +como @emph{parejas}. El @code{car} del desplazamiento es la coordenada X, y el @code{cdr} es la coordenada Y. @example @@ -877,7 +965,17 @@ 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}. +@file{scm/lily-library.scm}. + +@subheading Fractions + +Fractions as used by LilyPond are again stored as @emph{pairs}, this +time of unsigned integers. While Scheme can represent rational numbers +as a native type, musically @samp{2/4} and @samp{1/2} are not the same, +and we need to be able to distinguish between them. Similarly there are +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 Dimensiones @@ -891,7 +989,7 @@ 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 +@file{scm/lily-library.scm}. Se deben usar estos procedimientos siempre que sea posible, para asegurar la consistencia del código. @subheading Listas-A de propiedades @@ -1019,18 +1117,16 @@ imprime lo siguiente: 'SequentialMusic 'elements (list (make-music - 'EventChord - 'elements + 'NoteEvent + 'articulations (list (make-music - 'NoteEvent - 'duration - (ly:make-duration 2 0 1 1) - 'pitch - (ly:make-pitch 0 0 0)) - (make-music 'AbsoluteDynamicEvent 'text - "f"))))) + "f")) + 'duration + (ly:make-duration 2 0 1 1) + 'pitch + (ly:make-pitch 0 0 0)))) @end example De forma predeterminada, LilyPond imprime estos mensajes sobre la @@ -1042,27 +1138,45 @@ salida a un archivo. lilypond archivo.ly >salida.txt @end example -Con la aplicación de un poco de formateo, la información anterior es -fácil de leer, +Con un poco de magia combinada de LilyPond y Scheme, podemos realmente +hacer que LilyPond dirija solamente esta salida a su propio archivo: + +@example +@{ + $(with-output-to-file "display.txt" + (lambda () #@{ \displayMusic @{ c'4\f @} #@})) +@} +@end example + + +Un poco de reformateo hace a la información anterior más fácil de +leer: @example (make-music 'SequentialMusic - 'elements (list (make-music 'EventChord - 'elements (list (make-music 'NoteEvent - 'duration (ly:make-duration 2 0 1 1) - 'pitch (ly:make-pitch 0 0 0)) - (make-music 'AbsoluteDynamicEvent - 'text "f"))))) + '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{@{ ... @}} 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 una expresión @code{EventChord} que contiene un objeto -@code{NoteEvent} (que almacena las propiedades de duración y altura) y -cualquier información adicional (en este caso, un evento -@code{AbsoluteDynamicEvent} con una propiedad @code{"f"} de texto. +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 @@ -1072,13 +1186,27 @@ cualquier información adicional (en este caso, un evento @c @emph{context} properties, and @emph{layout} properties. These @c are potentially confusing. -El objeto @code{NoteEvent} es el primer objeto de la propiedad -@code{'elements} de @code{someNote}. +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 + +The @code{NoteEvent} object is the representation of @code{someNote}. +Straightforward. How about putting c' in a chord? + +@example +someNote = +\displayMusic \someNote +===> (make-music 'EventChord 'elements @@ -1090,6 +1218,9 @@ someNote = c' (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. @@ -1136,7 +1267,7 @@ d' @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 +como @code{a} en @code{@{ a( a) @}}. Comenzamos examinando la representación interna de la música con la que queremos terminar. @example @@ -1146,71 +1277,68 @@ representación interna de la música con la que queremos terminar. 'SequentialMusic 'elements (list (make-music - 'EventChord - 'elements + 'NoteEvent + 'articulations (list (make-music - 'NoteEvent - 'duration - (ly:make-duration 2 0 1 1) - 'pitch - (ly:make-pitch 0 5 0)) - (make-music 'SlurEvent 'span-direction - -1))) + -1)) + 'duration + (ly:make-duration 2 0 1 1) + 'pitch + (ly:make-pitch 0 5 0)) (make-music - 'EventChord - 'elements + 'NoteEvent + 'articulations (list (make-music - 'NoteEvent - 'duration - (ly:make-duration 2 0 1 1) - 'pitch - (ly:make-pitch 0 5 0)) - (make-music 'SlurEvent 'span-direction - 1))))) + 1)) + 'duration + (ly:make-duration 2 0 1 1) + 'pitch + (ly:make-pitch 0 5 0)))) @end example -Las malas noticias son que las expresiones @code{SlurEvent} se deben -añadir @q{dentro} de la nota (o para ser más exactos, dentro de la -expresión @code{EventChord}). +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 - 'SequentialMusic - 'elements - (list (make-music - 'EventChord - 'elements - (list (make-music - 'NoteEvent - 'duration - (ly:make-duration 2 0 1 1) - 'pitch - (ly:make-pitch 0 5 0)))))) + '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{'elements} de cada una de +@code{SlurEvent} a la propiedad @code{'articulations} de cada una de ellas, y por último hacer una secuencia @code{SequentialMusic} con los -dos @code{EventChords}. +dos @code{EventChords}. 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 (parser location note) (ly:music?) "Return: @{ note ( note ) @}. - `note' is supposed to be an EventChord." + `note' is supposed to be a single note." (let ((note2 (ly:music-deep-copy note))) - (set! (ly:music-property note 'elements) + (set! (ly:music-property note 'articulations) (cons (make-music 'SlurEvent 'span-direction -1) - (ly:music-property note 'elements))) - (set! (ly:music-property note2 'elements) + (ly:music-property note 'articulations))) + (set! (ly:music-property note2 'articulations) (cons (make-music 'SlurEvent 'span-direction 1) - (ly:music-property note2 'elements))) + (ly:music-property note2 'articulations))) (make-music 'SequentialMusic 'elements (list note note2)))) @end example @@ -1222,7 +1350,11 @@ doubleSlur = #(define-music-function (parser location note) (ly:music?) La manera fácil de añadir articulación a las notas es mezclar dos expresiones musicales en un solo contexto, como se explica en @ruser{Crear contextos}. Sin embargo, supongamos que queremos -escribir una función musical que lo haga. +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{#@{...#@}} es como una @code{\variable} normal en la notación clásica de LilyPond. @@ -1249,61 +1381,54 @@ Scheme. Empezamos examinando nuestra entrada y la salida deseada, \displayMusic c4 ===> (make-music - 'EventChord - 'elements - (list (make-music - 'NoteEvent - 'duration - (ly:make-duration 2 0 1 1) - 'pitch - (ly:make-pitch -1 0 0)))) + 'NoteEvent + 'duration + (ly:make-duration 2 0 1 1) + 'pitch + (ly:make-pitch -1 0 0)))) ===== % desired output \displayMusic c4-> ===> (make-music - 'EventChord - 'elements + 'NoteEvent + 'articulations (list (make-music - 'NoteEvent - 'duration - (ly:make-duration 2 0 1 1) - 'pitch - (ly:make-pitch -1 0 0)) - (make-music 'ArticulationEvent 'articulation-type - "marcato"))) + "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{EventChord}, con una expresión @code{NoteEvent} en su lista de -elementos. Para añadir una articulación de marcato, se debe añadir -una expresión @code{ArticulationEvent} a la propiedad elements de la -expresión @code{EventChord}. +@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-marcato event-chord) - "Add a marcato ArticulationEvent to the elements of `event-chord', - which is supposed to be an EventChord expression." - (let ((result-event-chord (ly:music-deep-copy event-chord))) - (set! (ly:music-property result-event-chord 'elements) - (cons (make-music 'ArticulationEvent - 'articulation-type "marcato") - (ly:music-property result-event-chord 'elements))) - result-event-chord)) +(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-marcato}, y tiene una variable -llamada @code{event-chord}. En Scheme, el tipo de variable suele +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 a marcato..." +"Add an accent..." @end example @noindent @@ -1311,85 +1436,102 @@ 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. -@example -(let ((result-event-chord (ly:music-deep-copy event-chord))) -@end example - -Se usa @code{let} para declarar las variables locales. Aquí usamos -una variable local, llamada @code{result-event-chord}, a la que le -damos el valor @code{(ly:music-deep-copy event-chord)}. -@code{ly:music-deep-copy} es una función específica de LilyPond, como -todas las funciones que comienzan por @code{ly:}. Se usa para hacer -una copia de una expresión musical. Aquí, copiamos @code{event-chord} -(el parámetro de la función). Recuerde que nuestro propósito es -añadir un marcato a una expresión @code{EventChord}. Es mejor no -modificar el @code{EventChord} que se ha dado como argumento, porque -podría utilizarse en algún otro lugar. - -Ahora tenemos un @code{result-event-chord}, que es una expresión -@code{NoteEventChord} y es una copia de @code{event-chord}. Añadimos -el marcato a su propiedad de la lista de @code{'elements}. +Se preguntará porqué 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! lugar valor-nuevo) +(set! place new-value) @end example -Aquí, lo que queremos establecer (el @q{lugar}) es la propiedad -@code{'elements} de la expresión @code{result-event-chord}. +Aquí, lo que queremos establecer (el @q{place}) es la propiedad +@code{'articulations} de la expresión @code{note-event}. @example -(ly:music-property result-event-chord 'elements) +(ly:music-property note-event 'articulations) @end example -@code{ly:music-property} es la función que se usa para acceder a las -propiedades musicales (los @code{'elements}, @code{'duration}, -@code{'pitch}, etc, que vimos en la salida de @code{\displayMusic} -anterior). El nuevo valor es la antigua propiedad @code{'elements}, -con un elemento adicional: la expresión @code{ArticulationEvent}, que -copiamos a partir de la salida de @code{\displayMusic}, +@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 "marcato") - (ly:music-property result-event-chord 'elements)) + 'articulation-type "accent") + (ly:music-property result-event-chord 'articulations)) @end example -@code{cons} se usa para añadir un elemento a una lista sin modificar -la lista original. Esto es lo que queremos: la misma lista que antes, -más la expresión @code{ArticulationEvent} nueva. El orden dentro de -la propiedad @code{'elements} no es importante aquí. +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 marcato a su -propiedad @code{elements}, podemos devolver @code{result-event-chord}, -de ahí la última línea de la función. +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-marcato} en una función -musical: +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 único argumento @q{real}). @example -addMarcato = #(define-music-function (parser location event-chord) +addAccent = #(define-music-function (parser location note-event) (ly:music?) - "Add a marcato ArticulationEvent to the elements of `event-chord', - which is supposed to be an EventChord expression." - (let ((result-event-chord (ly:music-deep-copy event-chord))) - (set! (ly:music-property result-event-chord 'elements) - (cons (make-music 'ArticulationEvent - 'articulation-type "marcato") - (ly:music-property result-event-chord 'elements))) - result-event-chord)) + "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 Podemos verificar que esta función musical funciona correctamente: @example -\displayMusic \addMarcato c4 +\displayMusic \addAccent c4 @end example - - - - @ignore @menu * Trucos con Scheme:: @@ -1397,7 +1539,7 @@ Podemos verificar que esta función musical funciona correctamente: @c @nod e Trucos con Scheme @c @sectio n Trucos con Scheme -@translationof Tweaking with 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 @@ -1417,7 +1559,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''' { @@ -1442,7 +1584,7 @@ tempoPadded = #(define-music-function (parser location padding tempotext) (number? string?) #{ \once \override Score.MetronomeMark #'padding = $padding - \tempo \markup { \bold $tempotext } + \tempo \markup { \bold #tempotext } #}) \relative c'' {