Emacs Lisp Intro Es
Emacs Lisp Intro Es
Emacs Lisp Intro Es
Copyright
c 2015 Libremanuals.
Publicado por:
Libremanuals, http://www.libremanuals.net/
Permission is granted to copy, distribute and/or modify this document under the
terms of the GNU Free Documentation License, Version 1.3 or any later version
published by the Free Software Foundation; there being no Invariant Section, with
the Front-Cover Texts being “A GNU Manual”, and with the Back-Cover Texts as
in (a) below. A copy of the license is included in the section entitled “GNU Free
Documentation License”.
(a) The FSF’s Back-Cover Text is: “You have the freedom to copy and modify this
GNU manual. Buying copies from the FSF supports it in developing GNU and
promoting software freedom.”
i
Prefacio . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
1 Procesamiento de listas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
2 Practicando evaluación . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
3 Cómo escribir definiciones de funciones. . . . . . . . . . . . . . . . . . . . . . . 26
4 Unas pocas funciones de buffer relacionadas . . . . . . . . . . . . . . . . . . 46
5 Unas pocas funciones más complejas . . . . . . . . . . . . . . . . . . . . . . . . . 57
6 Encogiendo y extendiendo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70
7 car, cdr, cons: Funciones fundamentales. . . . . . . . . . . . . . . . . . . . . 74
8 Cortando y almacenando texto . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82
9 Cómo las listas se implementan . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104
10 Pegando texto. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 108
11 Bucles y recursión . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 111
12 Búsquedas de expresiones regulares . . . . . . . . . . . . . . . . . . . . . . . . . 135
13 Contando: repetición y regexps . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 150
14 Contando palabras en una defun . . . . . . . . . . . . . . . . . . . . . . . . . . . 162
15 Leyendo un grafo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 182
16 Tu fichero .emacs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 191
17 Depurando . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 209
18 Conclusión . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 215
A La función the-the . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 217
B Manejando el anillo de la muerte . . . . . . . . . . . . . . . . . . . . . . . . . . . 219
C Un grafo con ejes etiquetados . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 227
D Software Libre y Manuales Libres . . . . . . . . . . . . . . . . . . . . . . . . . . . 248
E GNU Free Documentation License . . . . . . . . . . . . . . . . . . . . . . . . . . 251
Índice . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 260
ii
Índice General
Prefacio . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
Leyendo este texto . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
Para quien está esto escrito . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
Historia de Lisp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
Una nota para principiantes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
Se agradece . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
1 Procesamiento de listas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
1.1 Listas Lisp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
1.1.1 Átomos Lisp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
1.1.2 Espacios en blanco en listas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
1.1.3 GNU Emacs te ayuda a escribir listas . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
1.2 Ejecutar un programa . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
1.3 Generar un mensaje de error . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
1.4 Nombres de sı́mbolos y definiciones de funciones . . . . . . . . . . . . . . . . . . . . . . . 6
1.5 El intérprete Lisp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
1.5.1 Compilación de bytes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
1.6 Evaluación . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
1.6.1 Evaluando listas propias . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
1.7 Variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
1.7.1 Mensaje de error para un sı́mbolo sin una función . . . . . . . . . . . . . . . 10
1.7.2 Mensaje de error para un sı́mbolo sin un valor. . . . . . . . . . . . . . . . . . . 10
1.8 Argumentos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
1.8.1 Tipos de argumentos de datos. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
1.8.2 Un argumento como el valor de una variable o lista . . . . . . . . . . . . . . 12
1.8.3 Número de variables de argumentos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
1.8.4 Usando el tipo incorrecto de objeto como un argumento . . . . . . . . . 13
1.8.5 La función message . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
1.9 Configurando el valor de una variable . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
1.9.1 Usando set. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
1.9.2 Usando setq . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
1.9.3 Contando . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
1.10 Resumen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
1.11 Ejercicios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
2 Practicando evaluación . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
2.1 Nombres de búffer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
2.2 Obteniendo búffers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
2.3 Cambiando búffers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
2.4 Tama~ no de búffer y la localización del punto . . . . . . . . . . . . . . . . . . . . . . . . . 24
2.5 Ejercicio . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
iii
6 Encogiendo y extendiendo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70
6.1 La forma especial save-restriction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70
6.2 what-line . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71
6.3 Ejercicio de encoger . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72
17 Depurando . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 209
17.1 depurar . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 209
17.2 debug-on-entry. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 210
17.3 debug-on-quit y (debug) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 211
17.4 El depurador de nivel de fuentes edebug . . . . . . . . . . . . . . . . . . . . . . . . . . . 212
17.5 Ejercicios de depuración . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 213
18 Conclusión . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 215
Índice . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 260
Leyendo este texto 1
Prefacio
La mayorı́a del entorno integrado GNU Emacs está escrito en el lenguaje de pro-
gramación llamado Emacs Lisp. El código escrito en este lenguaje de programación
es el software—el conjunto de instrucciones—que cuenta al ordenador qué hacer
cuando tu le das comandos. Emacs está dise~ nado de forma que se puede escribir
nuevo código en Emacs Lisp y fácilmente instalarlo como una extensión al editor.
(GNU Emacs se define muchas veces como un “editor extensible”, pero hace mu-
cho más que proporcionar capacidad de edición. Es mejor referirse a Emacs como
un “entorno de computación extensible”. Sin embargo, esta frase es un poco preten-
ciosa. Es más fácil referirse a Emacs simplemente como un editor. De hecho, cada
cosa que se hace en Emacs—encontrar la fecha Maya y fases de la luna, simplificar
polinomios, depurar código, administrar ficheros, leer cartas, escribir libros—todas
estas actividades son maneras de editar en un sentido amplio de la palabra.)
Aunque Emacs Lisp normalmente se asocia solo con Emacs, es un lenguaje
de programación completo. Se puede usar Emacs Lisp del mismo modo que con
cualquier otro lenguaje de programación.
Quizás se quiere comprender la programación; quizás se quiere extender Emacs;
o quizás se quiere llegar a ser un programador. Esta introducción a Emacs Lisp está
dise~
nada para ayudar a empezar en todo esto: para guiarse en el aprendizaje de los
fundamentos de programación, y de manera más importante, para ense~ nar como
uno mismo puede ir más allá.
como M-. (la clave que invoca el comando find-tag). También se aprende acerca
de búffers y otros objetos que son parte del entorno. Aprender estas funcionalidades
de Emacs es como aprender nuevas rutas alrededor de tu hogar.
Finalmente, espero poder transmitir algunas habilidades de Emacs para apren-
der aspectos de programación que no se conocen. Con frecuencia se puede usar
Emacs para ayudar a comprender qué puzzles encontrar o como hacer alguna cosa
nueva. Este auto-descubrimiento no es solo un placer, también es una ventaja.
Historia de Lisp
Lisp fué originariamente desarrollado en los 50 en el Instituto Tecnológico de Massa-
chusetts para investigar en inteligencia artificial. El gran poder del lenguaje Lisp lo
hace superior para otros propósitos también, tal como escribir comandos de edición
y entornos integrados.
GNU Emacs Lisp está fuertemente inspirado en Maclisp, que está escrito en el
MIT en los sesenta. Está en cierto modo inspirado en Common Lisp, que llega a
ser un estándar en los 80. Sin embargo, Emacs Lisp es mucho más simple que Com-
mon Lisp. (La distribución estándar de Emacs contiene un fichero de extensiones
opcional, cl.el, que a~ nade muchas funcionalidades a Emacs Lisp.)
Una nota en terminologı́a: cuando uso la palabra Lisp sola, con frecuencia me
estoy refiriendo a los dialectos de Lisp en general, pero cuando yo hablo de Emacs
Lisp, me estoy refiriendo a GNU Emacs Lisp en particular.
Se agradece
Estoy agradecido a todas las personas que me ayudaron con este libro. Especial-
mente agradecido a Jim Blandy, Noah Friedman, Jim Kingdon, Roland McGrath,
Frank Ritter, Randy Smith, Richard M. Stallman, y Melissa Weisshaus. Gracias
también a Philip Johnson y David Stampe por su ánimo paciente. Mis errores son
mı́os.
Robert J. Chassell
[email protected]
Sección 1.1: Listas Lisp 1
1 Procesamiento de listas
Para quienes no están habituados, Lisp es un lenguaje de programación extra~ no.
En código Lisp hay paréntesis por todas partes. Algunas personas incluso reclaman
que el nombre signfica ‘Lots of Isolated Silly Parentheses’ (‘Montones de Paréntesis
Aislados Estúpidos’). Pero la protesta no tiene fundamento. Lisp es para procesa-
miento de listas, y el lenguaje de programación maneja listas (y listas de listas)
poniéndolas entre paréntesis. Los paréntesis marcan los lı́mites de la lista. Algunas
veces una lista va precedida por un apóstrofe simple o una marca de cita, ‘’’1 Las
listas son el fundamento de Lisp.
ser divididas en peque~ nas partes, sin perder su significado dentro del programa;
lo mismo ocurre con números y caracteres de sı́mbolos simples como ‘+’. Por otro
lado, a diferencia de un átomo, una lista puede ser dividida en peque~
nas partes. Ver
Sección “car” in Funciones Fundamentales.
En una lista, los átomos se separan unos de otros por espacios en blanco. Pueden
ir pegados a un paréntesis.
En realidad, los átomos que componen nuestro universo se llamaron de ese modo
cuando se penso que serı́an indivisibles; pero han sido encontrados átomos fı́sicos
que no son indivisibles. Las partes pueden dividir un átomo o puede fisionarse en
2 partes de igual tama~ no. Los átomos fı́sicos se nombraron prematuramente, antes
de que su verdadera naturaleza fuese encontrada. En Lisp, ciertos tipos de átomos,
como un array, pueden ser separados en partes; pero el mecanismo de hacer esto es
diferente de el mecanismo para dividir una lista. Tan lejos como las operaciones de
las listas son concebidas, los átomos de una lista son indivisibles.
Como en espa~ nol, los significados de las letras que componen un átomo Lisp
son diferentes desde el significado de las letras compuestas como una palabra. Por
ejemplo, la expresión ‘ay’, es completamente diferente de las dos palabras ‘a’, e ‘y’.
Hay muchos tipos de átomos naturales, pero solo unos pocos en Lisp: por ejem-
plo, números, tales como 37, 511, o 1729, y sı́mbolos, tales como ‘+’, ‘foo’, o
‘forward-line’. Las palabras que hemos listado en los ejemplos de debajo son todos
sı́mbolos. Cada dı́a de conversación Lisp, la palabra “átomo” no se usa con frecuen-
cia, porque los programadores normalmente intentan ser más especı́ficos acerca de
que tipo de átomo están tratando. La programación Lisp es sobre todo de sı́mbolos
(y algunas veces números) con listas. (De ese modo, tres palabras rodeadas de
paréntesis son una apropiada lista en Lisp, desde que ello consiste en átomo, que en
este caso son sı́mbolos, separados por espacios en blanco y cerrados por paréntesis,
sin cualquier puntuación no Lisp.)
Sección 1.1: Listas Lisp 3
una definición de función adjunta a eso, el lugar que contendrı́a las instrucciones es
‘vacı́o’.
Por otro lado, desde que fuimos capaces de a~ nadir 2 más 2 de manera exitosa,
evaluando (+ 2 2), se puede inferir que el sı́mbolo + debe tener un conjunto de
instrucciones para que el ordenador obedezca y estas instrucciones deben a~ nadir los
números que siguen el +.
Es posible prevenir que Emacs entre en el depurador en casos como este. No
se explicará cómo hacer esto aquı́, pero se mencionará que el resultado se parece,
porque se puede encontrar una situación similar si hay un error en algún código
Emacs que se está usando. En tales casos, se verá una lı́nea del mensaje de error;
que aparecerá en el área echo y parecerá ası́:
La definición de la función de sı́mbolos está
vacı́o: this
El mensaje aparece tan pronto se escribe una tecla, incluso para mover el cursor.
Conocemos el significado de la palabra ‘Sı́mbolo’. Se refiere al primer átomo
de la lista, la palabra ‘este’. La palabra ‘función’ se refiere a las instrucciones
que cuentan al ordenador que hacer. (Técnicamente, el sı́mbolo cuenta al ordena-
dor donde encontrar las instrucciones, pero esto es una complicación que podemos
ignorar por el momento.)
El mensaje de error puede ser comprendido: ‘La definición del sı́mbolo está
vacı́o: este’. El sı́mbolo (que es, la palabra ‘este’) le faltan instrucciones para que
el ordenador funcione.
los nombres de funciones que tratan con Texinfo empiezan con ‘texinfo-’ y estas
funciones que tratan con la lectura de correo empiezan con ‘rmail-’.
extensión .elc en vez de una extensión .el. Verás ambos tipos de ficheros en el
directorio emacs/lisp; los ficheros para leer estos son con extensiones .el.
Como una cuestión práctica, para la mayorı́a de las cosas tu podrı́as personalizar
o extender Emacs, no necesitas compilar byte; y no discutirás el asunto aquı́. Ver
Sección “Compilación de Byte” in El Manual de Referencia de GNU Emacs, para
una completa descripción de compilación byte.
1.6 Evaluación
Cuando el intérprete Lisp funciona en una expresión, el término para la actividad
es llamada evaluación. Decimos que el intérprete ‘evalúa la expresión’. Yo he usado
este término varias veces antes. La palabra viene desde su uso en el lenguaje de
cada dı́a, ‘para cierto valor o cantidad de; para estimar’ de acuerdo a Webster’s
New Collegiate Dictionary.
Después de evaluar una expresión, el intérprete Lisp normalmente devuelve el
valor que el ordenador produce trayendo las instrucciones encontradas en la defi-
nición de la función, o quizás dará esta función y producirá un mensaje de error.
(El intérprete puede también quedarse colgado, ası́ hablar, a una función diferente
o puede intentar repetir continuamente que está haciendo para siempre y siempre
en lo que está llamado como un ‘bucle infinito’. Estas acciones son menos comunes;
y pueden ignorarse). Más frecuentemente, el intérprete devuelve un valor.
Al mismo tiempo el intérprete devuelve un valor, puede hacer cualquier cosa
más también, tal como mover un cursor o copiar un fichero; este otro tipo de acción
es llamada efecto lateral. Acciones que los humanos pensamos que son importantes
tales como imprimir resultados son, con frecuencia, “efectos laterales” al intérprete
Lisp. La jerga puede sonar peculiar, pero cambiar que es fácil aprender a usar efectos
laterales.
En resumen, evaluando una expresión simbólica normalmente causa que el
intérprete devuelva un valor y quizás trajo un efecto lateral; o al menos produ-
ce un error.
1.7 Variables
En Emacs Lisp, un sı́mbolo puede tener un valor adjunto como puede tener una
definición de función adjunta. Las dos son diferentes. La definición de función es
un conjunto de instrucciones que un ordenador obedece. Un valor, por otro lado,
es alguna cosa como un número o un nombre, que puede variar (que es porque tal
sı́mbolo es llamado variable). El valor de un sı́mbolo puede ser una expresión en
Lisp, tal como un sı́mbolo, número, lista, o cadena. Un sı́mbolo que tiene un valor
es con frecuencia llamado una variable.
Un sı́mbolo puede tener ambos una definición de función y un valor adjunto al
mismo tiempo. O puede tener solo uno u otro. Los dos están separados. Esto es algo
similar al camino, el nombre Cambridge puede referirse a la ciudad en Massachusetts
y tener alguna información adjunta al nombre tan bien, tal como “gran centro de
programación”.
Otro camino para pensar acerca de esto es imaginar un sı́mbolo como ser una
caja de cajones. La definición de función es poner en el cajón que maneja el valor
que puede ser cambiado sin afectar los contenidos del cajón que maneja la definición
de función, y viceversa.
La variable fill-column ilustra un sı́mbolo con un valor adjunto: en cada buffer
de GNU Emacs, este sı́mbolo establece algún valor, normalmente 72 o 70, pero
algunas veces algún otro valor. Para encontrar el valor de este sı́mbolo, evalúalo por
sı́ mismo. Si está leyendo esto en Info dentro de GNU Emacs, tu puedes hacer esto
poniendo el cursor después del sı́mbolo y escribiendo C-x C-e:
10 Capı́tulo 1: Procesamiento de listas
fill-column
Después de que yo escribiera C-x C-e, Emacs imprimió el número 72 en mi área echo.
Este es el valor por el que fill-column es escogido para mi, porque yo lo escribo.
Puede ser diferente para ti en tu búffer Info. Sepa que el valor devuelto como una
variable es impreso exactamente por el mismo camino que el valor devuelto por una
función trayendo sus instrucciones. Puesto que el punto de vista del intérprete Lisp,
es un valor devuelto. El tipo de expresión viene de ceder a la cuestión una vez el
valor se conoce.
Un sı́mbolo puede tener cualquier valor adjunto a ello o, usar la jerga, se puede
bind (asociar) la variable a un valor: a un número, tal como 72; a una cadena, \"tal
como esta\"; a una lista, tal como (abeto pino roble); podemos incluso asociar
una variable a una definición de función.
Un sı́mbolo puede ser emparejado por un valor en varios caminos. Ver Sección 1.9
“Configurando el valor de una variable”, página 16, para información acerca de un
camino para hacer esto.
1.8 Argumentos
Para ver cómo la información pasa a funciones, se permite mirar de nuevo a nuestro
viejo standby, la adición de dos más dos. En Lisp, esto es escrito como sigue:
(+ 2 2)
Si se evalúa esta expresión, el número 4 aparecerá en tu área echo. Lo que el
intérprete de Lisp hace es a~ nadir los número que sigue el +.
Los números a~ nadidos por + son llamados argumentos de la función +. Estos
números son la información que es dada para o pasada a la función.
La palabra ‘argumento’ viene del ámbito de las matemáticas y no se refiere a
una disputa entre 2 personas, sino que se refiere a la información presentada a la
función, en este caso, al +. En Lisp, los argumentos a una función son los átomos o
listas que siguen la función. Los valores devueltos por la evaluación de estos átomos
o listas son pasados a la función. Funciones diferentes requieren diferentes números
de argumentos; algunas funciones no requieren ninguno más.2
2
Es curioso trazar la ruta por la que la palabra ‘argumento’ viene para tener dos signi-
ficados diferentes, uno en matemáticas y el otro en el inglés de cada dı́a. De acuerdo al
Oxford English Dictionary, la palabra deriva del Latı́n para ‘clarificar’, de este mo-
do significa, por un hilo de derivación, viene a significar ‘asertir de una manera contra
otro que puede crear un contador de aserciones’, que lidera el significado de la palabra
como una disputa. (Nótese aquı́ que la palabra Inglés tiene dos definiciones diferentes
adjuntas al mismo tiempo. En contraste, en Emacs Lisp, un sı́mbolo no puede tener
dos definiciones de funciones diferentes al mismo tiempo.)
12 Capı́tulo 1: Procesamiento de listas
(*) ⇒ 1
En este conjunto, las funciones tienen un argumento cada una:
(+ 3) ⇒ 3
(* 3) ⇒ 3
En este conjunto, las funciones tienen tres argumentos cada una:
(+ 3 4 5) ⇒ 12
(* 3 4 5) ⇒ 60
Como es normal, el mensaje de error intenta ser útil y tiene sentido después de
que se aprenda cómo leerlo.3
La primera parte del mensaje de error es honesto; dice ‘wrong type argument’
(mal tipo de argumento). Lo siguiente viene con la misteriosa palabra de jerga
‘number-or-marker-p’. Esta palabra está intentando contar qué tipo de argumento
+ se espera.
El sı́mbolo number-or-marker-p dice que el intérprete Lisp está intentando
determinar si la información presentada (el valor del argumento) es un número o
una marca (un objeto especial representando una posición de buffer). Lo que hace
es testear para ver si el + está siendo números dados para a~ nadir. También chequea
para ver si el argumento es algo llamado un marcador, que es una funcionalidad
especı́fica de Emacs Lisp. (En Emacs, las localizaciones en un búffer son grabadas
como marcadores. Cuando la marca está asignado con el atajo C-@ o C-SPC, su
posición se guarda como un marcador. La marca puede ser considerada un número—
el número de caracteres la localización está desde el principio del búffer.) En Emacs
Lisp, + puede ser usado para a~ nadir el valor numérico de posiciones de marca como
números.
La ‘p’ de number-or-marker-p es el cuerpo de una práctica iniciada en los
primeros dı́as de la programación Lisp. La ‘p’ es para ‘predicado’. En la jerga usada
por los primeros investigadores de Lisp, un predicado se refiere a una función para
determinar si alguna propiedad es verdadera o falsa. Ası́ la ‘p’ nos cuenta que
number-or-marker-p es el nombre de una función que determina que el argumento
dado es un número o una marca. Otros sı́mbolos Lisp que finalizan en ‘p’ incluyen
zerop, una función que chequea si sus argumentos tienen el valor de cero, y listp,
una función que prueba si sus argumentos son una lista.
Finalmente, la última parte del mensaje de error es el sı́mbolo hello. Esto es el
valor del argumento que pasaba a +. Si la adición habı́a sido pasada al tipo correcto
de objeto, el valor pasado habrı́a sido un número, tal como 37, en vez de un sı́mbolo
como hello. Pero entonces tu no habrı́as obtenido el mensaje de error.
3
(quote hola) es una expresión de la abreviación ’hola.
Sección 1.8: Argumentos 15
4
Actualmente, puede usar %s para imprimir un número. Eso es no especı́fico. %d imprime
solo la parte de un número a la izquierda de un punto decimal, y no cualquier cosa que
no es un número.
16 Capı́tulo 1: Procesamiento de listas
la forma especial setq. Esta forma especial es solo como set excepto que el primer
argumento se cita automáticamente, ası́ no se necesita escribir la marca de cita por
uno mismo. También, como una conveniencia a~ nadida, setq permite asignar varias
variables diferentes a diferentes valores, todo en una expresión.
Para asignar el valor de la variable carnı́voros a la lista ’(leon tigre
leopardo) usando setq, la siguiente expresión que se usa es:
(setq carnivoros ’(leon tigre leopardo))
Esto es exactamente lo mismo que usar set excepto que el primer argumento se
cita automáticamente por setq. (El ‘q’ en setq significa quote.)
Con set, la expresión que se verı́a es:
(set ’carnivoros ’(leon tigre leopardo))
También, setq puede ser usado para asignar diferentes valores a diferentes va-
riables. El primer argumento está asociado al valor del segundo argumento, el tercer
argumento se asocia al valor del cuarto argumento, y ası́. Por ejemplo, se podrı́a
usar lo siguiente para asignar una lista de árboles al sı́mbolo arboles y una lista
herbı́voros al sı́mbolo herbivoros:
(setq arboles ’(pino abeto roble arce)
herbivoros ’(gacela antilope cebra))
(La expresión podrı́a también haber estado en una lı́nea, pero podrı́a no haberse
ajustado en una página; y los humanos encuentran que es fácil leer listas formatea-
das.)
Aunque yo he estado usando el término ‘assign’, hay otro camino de pensar
acerca de los trabajos de set y setq; y que es decir que set y setq creen el
sı́mbolo point a la lista. Este camino posterior de pensamiento es muy común y
en los capı́tulos siguientes volveremos al menos a un sı́mbolo que tiene un valor,
especı́ficamente una lista, adjunta; o, expresa otros caminos, el sı́mbolo asigna a
“apuntar” a la lista.
1.9.3 Contando
Aquı́ hay un ejemplo que muestra cómo usar setq en un contador. Se podrı́a usar
esto para contar cuantas veces una parte de un programa se repite por sı́ mismo.
Primero asigna una variable a cero; entonces a~nade uno al número cada vez que el
programa se repite ası́ mismo. Para hacer esto, se necesita una variable que sirve
como un contador, y dos expresiones: una expresión setq que asigna la variable
contador a cero; y una segunda expresión setq que incrementa el contador cada vez
que eso es evaluado.
(setq counter 0) ; Se inicializa.
counter ; Contador.
(El siguiente texto el ‘;’ son comentarios. Ver Véase Sección 3.2.1 “Cambiar una
definición de función”, página 29.)
Si evalúas la primera de estas expresiones, el inicializador, (setq counter 0),
y entonces evalúa la tercera expresión, counter, el número 0 aparecerá en el área
echo. Si entonces se evalúa la segunda expresión, el incrementador, (setq counter
18 Capı́tulo 1: Procesamiento de listas
1.10 Resumen
Aprender Lisp es como escalar una monta~ na en la que la primera parte es la empi-
nada. Ahora has escalado la parte más difı́cil; y lo que queda es más fácil.
En resumen,
• Los programas Lisp son hechos de expresiones, que son listas o átomos simples.
• La listas son hechas de cero o más átomos o listas internas, separadas por
espacios en blanco y rodeados de paréntesis. Una lista puede estar vacı́a.
• Los átomos son sı́mbolos multi-carácter, como forward-paragraph, los
sı́mbolos de caracteres como +, cadenas de caracteres entre marcas de comillas
dobles, o números.
• Un número se evalúa a sı́ mismo.
• Una cadena entre comillas dobles también se evalúa a sı́ mismo.
• Cuando se evalúa un sı́mbolo a sı́ mismo, su valor se devuelve.
• Cuando se evalúa una lista, el intérprete mira el primer sı́mbolo en la lista y en
la definición de la función asociada a este sı́mbolo. Ası́ se traen las instrucciones
en la definición función.
• Una marca de cita (quote) simple, ’ , cuenta al intérprete Lisp que devolverı́a
la siguiente expresión como se escribe, y se evalúa como si la cita no estuviera.
• Los argumentos son la información pasada a una función. Los argumentos a
una función son computados evaluando el resto de los elementos de la lista de
la que la función es el primer elemento.
• Una función siempre devuelve un valor cuando se evalúa (a menos que obten-
ga un error); además, puede también traer alguna acción llamada un “efecto
colateral”. En muchos casos, un propósito de la función primaria es crear un
efecto colateral.
1.11 Ejercicios
Unos pocos ejercicios simples:
• Genera un mensaje de error evaluando un sı́mbolo apropiado que no está entre
paréntesis.
Sección 1.11: Ejercicios 19
2 Practicando evaluación
Antes de aprender como escribir una definición de función en Emacs Lisp, es útil
gastar un poco de tiempo evaluando varias expresiones que ya han sido escritas.
Estas expresiones serán listas con las funciones como su primer (y con frecuencia
único) elemento. Desde alguna de las funciones asociadas con búffers son ambas
simples e interesantes, nosotros empezaremos con estas. En esta sección, evaluare-
mos unas pocas de estas. En otra sección, estudiaremos el código de varios otros
funciones relacionadas con búffers, para ver cómo fueron escritas.
Siempre y cuando se de un comando de edición a Emacs Lisp, tal como el
comando para mover el cursor o el scroll de la pantalla, se está evaluando una
expresión, el primer elemento del que es una función. Ası́ es cómo Emacs funciona.
Cuando se escriben teclas, se causa que el intérprete Lisp evalúe una expresión
que devuelve resultados. Incluso escribiendo texto plano se evalúa la función Emacs
Lisp, en este caso, uno usa self-insert-command, simplemente inserta el carac-
ter que se escribió. Las funciones que se evalúan escribiendo atajos de teclado se
llaman funciones interactive, o comandos; como crear una función interactive será
ilustrada en el capı́tulo sobre cómo escribir definiciones de funciones. Ver Sección 3.3
“Creando una Función Interactive”, página 29.
Además de escribir comandos de teclado, se ha visto un segundo camino para
evaluar una expresión: posicionar el cursor después de una lista y escribiendo C-x
C-e. Esto es lo que se hará en el resto de esta sección. Hay otros caminos de evaluar
una expresión también; que serán descritos como vienen.
Siendo usadas para evaluación práctica, las funciones mostradas en las siguientes
pocas secciones son importantes en su propio derecho. Un estudio de estas funciones
hace clara la distinción entre búffers y ficheros, cómo cambiar a un búffer, y como
determinar una localización con ella.
Si está leyendo esto en Info dentro de GNU Emacs, se puede evaluar cada una
de las siguientes expresiones posicionando el cursor después de eso y escribiendo
C-x C-e.
(buffer-name)
(buffer-file-name)
Cuando hago esto en Info, el valor devuelto para la evaluación de (buffer-name)
es "*info", y el valor devuelto por evaluar (buffer-file-name) es nil.
Por otro lado, mientras se está escribiendo este documento, el valor de-
vuelto por la evaluación de (buffer-name) es "introduction.texinfo",
y el valor devuelto por la evaluación (buffer-file-name) es
"/gnu/work/intro/introduction.texinfo".
La forma es el nombre del búffer y lo posterior es el nombre del fichero. En Info,
el nombre búffer es "*info*". Info no apunta a cualquier fichero, ası́ el resultado de
evaluar (buffer-file-name)] es nil. El sı́mbolo nil es desde la palabra latina para
‘nada’; en este caso, significa que el búffer no está asociado con cualquier fichero.
(En Lisp, nil también se usa con el significado de ‘falso’ y es sinómino de la lista
vacı́a, ().)
Cuando estoy escribiendo, el nombre de mi búffer es
"introduction.texinfo". El nombre del fichero al que se apunta es
"/gnu/work/intro/introduction.texinfo".
(En las expresiones, los paréntesis hacen que el intérprete Lisp trate a
buffer-name y buffer-file-name como funciones; sin los paréntesis, el intérprete
intentarı́a evaluar los sı́mbolos como variables. Ver Sección 1.7 “Variables”,
página 9.)
A pesar de la distinción entre ficheros y búffers, con frecuencia se encuentra
que hay personas que se refieren a un fichero cuando quieren un búffer y al revés.
En realidad, la mayorı́a de las personas dicen, “Yo estoy editando un fichero,” en
vez de decir, “Estoy editando un búffer que pronto se guardará en un fichero.” Es
casi siempre claro desde el contexto que las personas quieren decir. Al tratar con
programas de ordenador, sin embargo, es importante tener la distinción en mente,
ya que el ordenador no es una persona inteligente.
La palabra ‘búffer’, por el camino, viene desde el significado de la palabra como
una cu~ na que mata la fuerza de una colisión. En los primeros ordenadores, un búffer
acu~naba la interacción entre ficheros y la unidad de procesamiento central. Los tam-
bores o cintas que manejan un fichero y la unidad de procesamiento central fueron
piezas de equipamiento que fueron muy diferentes a cualquier otra, trabajando a sus
propias velocidades. El búffer se hizo posible para ellos a trabajar juntos de manera
efectiva. Finalmente, el búffer creció siendo uno intermedio, un lugar tomado tem-
poralmente, para ser el lugar donde el trabajo está hecho. Esta transformación es
como un peque~ no puerto de mar que crece dentro de una gran ciudad: una vez fué
el lugar donde la carga era depositada temporalmente antes de ser cargada dentro
de los barcos; entonces eso llega a ser un negocio y centro cultural en su propio
derecho.
22 Capı́tulo 2: Practicando evaluación
No todos los búffers están asociados con ficheros. Por ejemplo, un búffer no
visita cualquier fichero. De manera similar, un búffer *Help* no está asociado con
cualquier fichero.
Anta~no, cuando se perdı́a un fichero ~/.emacs y se empezaba una sesión Emacs
escribiendo el comando emacs solo, sin nombrar ficheros Emacs iniciaba con el búffer
*scratch* visible. En estos dı́as, se ve una página de inicio. Se puede seguir uno
de los comandos sugeridos en dicha pantalla, visitar un fichero, o presionar la barra
espaciadora para lograr el búffer *scratch*.
Si se cambia al búffer *scratch*, se escribe la posición del (buffer-name) del
cursor después, y entonces escribe C-x C-e para evaluar la expresión. El nombre
*scratch* será devuelto y aparecerá en el área echo. *scratch* es el nombre del
búffer. Cuando se escribe (buffer-file-name) en el búffer *scratch* y evalúa que,
nil aparecerá en el área echo, solo se hace cuando se evalúa (buffer-file-name)
en Info.
Incidentalmente, si se está en el búffer *scratch* y se quiere el valor devuelto
por una expresión para aparecer en el búffer en sı́ del área echo, se escribe C-u C-x
C-e en vez de C-x C-e. Esto causa que el valor devuelto aparezca después de la
expresión. El búffer se verá ası́:
(buffer-name)"*scratch*"
No se puede hacer esto en Info desde que Info es de solo lectura y no se permitirá
que se cambien los contenidos en el búffer. Pero puedes hacer esto en cualquier
búffer que se puede editar; y cuando se escribe código de documentación (tal como
este libro), esta funcionalidad es muy útil.
1
Actualmente, por defecto, si el búffer desde el que tu has cambiado es visible para tı́
en otra ventana, other-buffer elegirá el búffer más reciente que no puedes ver; esto
es algo peque~
no que he olvidado.
2
O incluso, para cambiar, solo se necesita pulsar RET si el buffer por defecto era
*scratch*, o si era diferente, entonces se puede escribir solo parte del nombre, tal
como *sc, luego presiona la tecla TAB para causar expandir al nombre completo, y
entonces escribe la tecla RET
24 Capı́tulo 2: Practicando evaluación
2.4 Tama~
no de búffer y la localización del punto
Finalmente, permı́tame en varias funciones simples, buffer-size, point,
point-min, y point-max. Estas dan información acerca del tama~ no de un búffer y
la localización del punto con eso.
La función buffer-size te cuenta el tama~no del búffer actual; que es, la función
un contaje del número de caracteres en el buffer.
(buffer-size)
Se puede evaluar esto en el camino usual, posicionando el cursor después de la
expresión y escribiendo C-x C-e.
En Emacs, la posición actual del cursor es llamada punto. La expresión (point)
devuelve un número que cuenta donde está localizado como un contaje del número
de caracteres desde el principio del búffer al punto.
3
Recuerda, esta expresión te permite cambiar a tus búffers más recientes y otros buffers
que no puedes ver. Si realmente quieres ir a tus búffers seleccionados más recientemente,
se necesita evaluar la siguiente expresión más compleja:
(switch-to-buffer (other-buffer (current-buffer)))
En este caso, el primer argumento a other-buffer cuenta de que búffer salir — el
actual — y el segundo argumento cuenta al other-buffer es OK para cambiar a un
búffer visible. En uso regular, switch-to-buffer toma a una ventana invisible desde
usarı́as C-x o (other-window) para ir a otro búffer visible
Sección 2.5: Ejercicio 25
2.5 Ejercicio
Encuentra un fichero con que tu estás trabajando y mueve hasta la mitad. Encuentra
el nombre de búffer, el nombre del fichero, tama~
no, y su posición en el fichero.
26 Capı́tulo 3: Cómo escribir definiciones de funciones
segunda lı́nea de una documentación, si tu tienes una, esto se ve cuando usas C-h f
(describe-function). La documentación es opcional, pero es también útil, deberı́a
ser incluido en casi cualquier función que se escribe.
La tercera lı́nea del ejemplo consiste en el cuerpo de la definición de función.
(La mayorı́a de las definiciones de funciones, de acuerdo, son más largas que esto.)
En esta función, el cuerpo es la lista, (* 7 number), que dice multiplicar el valor de
número por 7. (En Emacs Lisp, * es la función para la multiplicación, solo como +
es la función de suma.
Cuando se usa la función multiply-by-seven, el argumento number evalúa para
el número actual que quiere ser usada. Aquı́ hay un ejemplo que muestra como
multiply-by-seven es usada; pero ¡no intentes evaluar esto primero!.
(multiply-by-seven 3)
El sı́mbolo número, especificado en la definición de función en la siguiente sección,
es dada o “emparejado a” el valor 3 en el uso actual de la función. Note que aunque
número estaba dentro de paréntesis en la definición de función, el argumento pa-
sado a la función multiply-by-seven no está entre paréntesis. Los paréntesis son
escritos en la definición de función ası́ el ordenador puede figurarse donde la lista
de argumentos finaliza y el resto de la definición de función empieza.
Si se evalúa este ejemplo, se obtendrá un mensaje error. (¡Ve adelante, pruébalo!)
Esto es porque hemos escrito la definición de función pero no le hemos contado
todavı́a al ordenador la definición — no se ha instalado (o ‘cargado’) la definición
de función en Emacs. Instalando una función es el proceso que cuenta al intérprete
Lisp la definición de la función. La instalación se describe en la siguiente sección.
tales como mover hacia adelante por una palabra o lı́nea, y no para el valor devuel-
to. Si el valor devuelto fuera mostrado en el área echo cada vez que escribiste una
tecla, distraerı́a mucho
Tanto el uso de la forma especial interactive y un camino para mostrar
un valor en el área echo puede ser ilustrada creando una versión interactiva de
multiply-by-seven.
Aquı́ está el código:
(defun multiply-by-seven (number) ; Versión Interactiva.
"Multiplicar NUMERO por siete."
(interactive "p")
(message "El resultado es %d" (* 7 number)))
Se puede instalar este código emplazando tu cursor después y escribiendo C-x C-
e. El nombre de la función aparecerá en tu área echo. Entonces, se puede usar
este código escribiendo C-u y un número y entonces escribiendo M-x multiply-by-
seven y presionando RET. La frase ‘El resultado es ...’ seguido por el producto
aparecerá en el área echo
Hablando más generalmente, se puede invocar a una función como ésta de dos
maneras:
1. Escribir un argumento prefijo que contiene el número a ser pasado, y entonces
escribir M-x y el nombre de la función, como con C-u 3 M-x forward-sentence;
o,
2. Escribe siempre de modo que la tecla/s de la función estén emparejadas, como
con C-u 3 M-e.
Ambos ejemplos solo trabajan mencionados idénticamente para mover puntos hacia
adelantes tres frases. (Desde multiply-by-seven no está emparejado a una tecla,
eso no podrı́a ser usado como un ejemplo de emparejar la tecla.
(Véase Sección 16.7 “Algunos Atajos de Teclas”, página 197, para aprender
como emparejar un comando a una tecla.)
Un argumento prefijo está pasado para una función interactive escribiendo la
tecla META seguido por un número, por ejemplo, M-3 M-e, o escribiendo C-u y en-
tonces un número, por ejemplo, C-u 3 M-e (si se escribe C-u sin un número, por
defecto a 4).
El argumento será un número. Esto significa que el sı́mbolo number será asociado
a un número en la lı́nea:
(message "El resultado es %d" (* 7 number))
Por ejemplo, si tu argumento prefijo es 5, el intérprete Lisp evaluará la lı́nea como
si fuera:
(message "El resultado es %d" (* 7 5))
(Si está leyendo esto en GNU Emacs, se puede evaluar esta expresión por sı́ misma.)
Primera, el intérprete evaluará la lista interna, que es (* 7 5). Esto devuelve un
valor de 35. Lo siguiente, evaluará la lista externa, pasando los valores de la segunda
y subsiguientes elementos de la lista a la función message.
Como se ha visto, message es una función Emacs Lisp especialmente dise~ nada
para enviar una lı́nea de mensaje a un usuario. (Véase Sección 1.8.5 “La función
message”, página 14) En resumen, la función message imprime su primer argumento
en el área echo como es, excepto para ocurrencia de ‘%d’, o ‘%s’ (y varios otras %-
secuencias que no hemos mencionado). Cuando se ve una secuencia de control, la
función mira al segundo argumento o subsiguiente e imprime el valor del argumento
en la localización en la cadena donde la secuencia de control está localizada.
En la función interactiva multiply-by-seven, la cadena de control es ‘%d’, que
requiere un número, y el valor devuelto evaluando (* 7 5) es el número 35. Por
consiguiente, el número 35 es impreso en lugar de ‘%d’ y el mensaje es ‘El resultado
es 35’.
(Nótese que cuando se llama a la función multiply-by-seven, el mensaje está
impreso sin comillas, pero cuando se llama a message, el texto es impreso con
dobles comillas. Esto es porque el valor devuelto por message aparece en el área
echo cuando se evalúa una expresión cuyo primer elemente es message; pero cuando
se embebió en una función, message se imprime el texto como un efecto lateral sin
comillas.)
3.6 let
La expresión let es una forma especial en Lisp que se necesitará para usar la
mayorı́a de las definiciones de función.
let se usa para adjuntar o emparejar un sı́mbolo para un valor en tal camino
que el intérprete no confundirá la variable con otra variable del mismo nombre que
no es parte de la función.
Para comprender por qué la forma especial let es necesaria, considere la situa-
ción en el que tu propio hogar que generalmente se refiere como ‘la casa’, como en
la frase, “La casa necesita pintura.” Si está visitando a un amigo y tu alojamiento
se refiere a ‘la casa’, él es amistoso para estar refiriéndose a su casa, no la suya, que
es, una casa diferente.
Si el amigo está refiriéndose a su casa y tu piensas que él está refiriéndose a su
casa, tu puedes estar dentro por alguna confusión. La misma cosa podrı́a ocurrir
en Lisp si una variable que es usada dentro de una función tiene el mismo que una
variable que es usada dentro de otra función, y las dos no se pretende referirse al
mismo valor. La forma especial let previene este tipo de confusión.
La forma especial let evita confusiones. let crea un nombre para una variable
local que ensombrece cualquier uso del mismo nombre fuera de la expresión let.
Esto es como comprender que siempre y cuando tu host se refiera a ‘la casa’, significa
su casa, no la tuya. (Sı́mbolos usados en listas de argumentos trabajan en el mismo
camino. Véase Sección 3.1 “La Forma Especial defun”, página 26.)
Las variable locales son creadas por una expresión let que retiene su valor solo
con la expresión let por sı́ misma (y con expresiones llamadas con la expresión
let); las variables locales no tiene efecto fuera de la expresión let.
Otro camino para pensar acerca de let es que es como un setq que es temporal
y local. Los valores asignado por let son automáticamente deshechos cuando el let
está finalizado. La configuración solo afecta a expresiones que están dentro de los
emparejamientos de la expresión let. En jerga de ciencia de computación, dirı́amos
que “el emparejamiento de un sı́mbolo es visible solo en funciones llamadas en la
forma let; en Emacs Lisp, el alcance es dinámico, no léxico.”
34 Capı́tulo 3: Cómo escribir definiciones de funciones
let puede crear más de una variable a la vez. También, let da cada variable
eso crea un valor inicial, si un valor especificado por tı́, o nil. (En la jerga, eso se
llama ‘asociar la variable al valor’.) Después let ha creado y asociado las variables,
ejecuta el código en el cuerpo del let y devuelve el valor de la última expresión en
el cuerpo, como el valor de la expresión let completa. (‘Ejecuta’ es un término de
jerga que significa evaluar una lista: viene desde el uso de la palabra significando
‘dar efecto práctico a’ (Diccionario de Inglés de Oxford). Desde que evalúas una
expresión para ejecutar una acción, ‘ejecuta’ ha evolucionado como un sinónimo
para ‘evaluar’.)
1
De acuerdo a Jared Diamond en Guns, Germs, y Steel, “. . . las cebras llegan a ser
muy peligrosas a medida que crecen” pero el clamor aquı́ son que ellos no llegan a ser
fieros como un tigre. (1997, W. W. Norton and Co., ISBN 0-393-03894-2, page 171)
36 Capı́tulo 3: Cómo escribir definiciones de funciones
Si está leyendo esto dentro de GNU Emacs, se puede evaluar la definición función en
el modo usual para instalarlo en Emacs, y entonces se puede evaluar las siguientes
dos expresiones para ver los resultados:
(tipo-de-animal ’fiera)
(tipo-de-animal ’cebra)
(tipo-de-animal ’fiera)
(tipo-de-animal ’cebra)
Sección 3.9: Verdad y falsedad en Emacs Lisp 39
(if nil
’true
’false)
40 Capı́tulo 3: Cómo escribir definiciones de funciones
Incidentalmente, si algún otro valor útil no está disponible para un test que
devuelve cierto, entonces el intérprete Lisp retornará el sı́mbolo t para cierto. Por
ejemplo, la expresión (> 5 4) devuelve t cuando se evalúa, como puedes ver eva-
luándolo en el camino usual:
(> 5 4)
Por otro lado, esta función devuelve nil si el test es falso.
(> 4 5)
3.10 save-excursion
La función save-excursion es la cuarta y última forma especial que se discutirá
en este capı́tulo.
En Emacs Lisp hay programas usados para edición, la función save-excursion
es muy común. Eso guarda la posición de punto y marca, ejecuta el cuerpo de
la función, y entonces restaura el punto y marca a sus posiciones previas si sus
posiciones fueran cambiadas. Su propósito primario es guardar que el usuario sea
sorprendido y molesto por movimientos inesperados de punto y marca.
Antes de discutir save-excursion, sin embargo, puede ser útil primero revisar
que punto y marca están en GNU Emacs. Punto es la posición actual del cursor.
En cualquier lugar que el cursor se posicione hay un punto. De manera más precisa,
en terminales donde el cursor parece estar en lo alto de un carácter, el punto está
inmediatamente antes del carácter. En Emacs Lisp, punto es un entero. El primer
carácter en un búffer es el número uno, el segundo es el número dos, y ası́. La
función punto devuelve la posición actual del cursor como un número. Cada búffer
tiene su propio valor para el punto.
La marca es otra posición en el búffer; su valor puede ser asignado con un
comando tal como C-SPC (set-mark-command). Si una marca ha sido asignada,
se puede usar el comando C-x C-x (exchange-point-and-mark) para hacer que el
cursor salte a la marca y asignar la marca para la posición previa del punto. Además,
si tu asignas otra marca, la posición puede ser guardada por este camino. Se puede
saltar al cursor para una marca guardada escribiendo C-u C-SPC una o más veces.
La parte del búffer entre el punto y la marca es llamada la región. Numerosos
comandos trabajan en la región, incluyendo center-region, count-lines-region,
kill-region y print-region.
La forma especial save-excursion salva las posiciones del punto y la marca y
restaura estas posiciones después del código con el cuerpo de la forma especial es
evaluada por el intérprete Lisp. De este modo, si el punto fuera en el principio de una
pieza de texto y algún código movido apunta al fin del búffer, el save-excursion
no apuntarı́a a donde fué antes, después las expresiones en el cuerpo de las que
fueron evaluadas.
En Emacs, una función frecuentemente mueve el punto como parte de sus traba-
jos internos incluso aunque un usuario no espere esto. Por ejemplo, count-lines-
region se mueve al punto. Para prevenir al usuario que se preocupe por el salto que
es inesperado y (desde el punto de vista del usuario) innecesario, save-excursion
es con frecuencia usado para punto y marca en la posición esperada por el usuario.
El uso de save-excursion es un buen guarda casas.
Sección 3.11: Revisar 41
Para estar seguro que la casa está limpia, save-excursion restaura los valores
de punto y marca incluso si alguna cosa va mal en el código dentro de eso (o,
para ser más preciso y usar la jerga apropiada, “en caso de salida anormal”). Esta
funcionalidad es muy útil.
Además grabando los valores de punto y marca, save-excursion guarda la traza
del actual buffer, y lo restaura, también. Esto significa que puedes escribir código
que cambiará el buffer y tener que save-excursion vuelva al buffer original. Esto
es como save-excursion es usado en append-to-buffer. (Véase Sección 4.4 “La
Definición de append-to-buffer”, página 50.)
3.11 Revisar
En los últimos pocos capı́tulos se han introducido un número limpio de funciones
y formas especiales. Aquı́ se han descrito brevemente, con unas pocas funciones
similares que no han sido mencionadas todavı́a.
eval-last-sexp
Evalúa la última expresión simbólica antes de la posición actual del
punto. El valor es impreso en el área echo a menos que la función sea
invocada con un argumento; en este caso, la salida es impresa en el
actual búffer. Este comando está normalmente asociado a C-x C-e.
defun Definir función. Esta forma especial ha subido a cinco partes: el nombre
una plantilla para los argumentos que serán pasados a la documenta-
42 Capı́tulo 3: Cómo escribir definiciones de funciones
interactive
Declara al intérprete que la función puede ser usada interactivamen-
te. Esta forma especial puede ser seguida por una cadena con una o
más partes que pasan la información a los argumentos de la función,
en secuencia. Estas partes pueden también contar al intérprete para
mostrar la información. Parte de las cadenas son separadas por nuevas
lı́neas, ‘\n’.
Caracteres de código común son:
let Declara que una lista de variables es para usarla con el cuerpo del let
y darles un valor inicial, bien nil o un valor especı́fico; entonces se
evaluarán el resto de las expresiones en el cuerpo del let y devolver el
valor de la última. Dentro del cuerpo del let, el intérprete Lisp no ve
los valores de las variables de los mismos nombres que son asociados
fuera del let.
Por ejemplo,
(let ((foo (nombre-de-buffer))
(bar (tamagno-de-buffer)))
(message
"Este buffer es %s y tiene %d caracteres."
foo bar))
Sección 3.11: Revisar 43
save-excursion
Graba los valores de punto y marca y el actual búffer antes de evaluar
el cuerpo de esta forma especial. Restaura los valores de punto y marca
y el búffer después de esto.
Por ejemplo,
(message "Hay %d caracteres dentro de este buffer."
(- (point)
(save-excursion
(goto-char (point-min)) (point))))
if Evalúa el primer argumento a la función; si es verdad, evalúa el segun-
do argumento; lo demás evalúa el tercer argumento, si hay uno.
La forma especial if es llamada condicional. Hay otros condicionales
en Emacs Lisp, pero if es quizás lo más comúnmente usado.
Por ejemplo,
(if (= 22 version-de-emacs-mayor)
(message "Esta es la versión 22 de Emacs")
(message "Esta no es la versión 22 Emacs"))
<
>
<=
>= La función < chequea si su primer argumento es más peque~ no que
su segundo argumento. Una función correspondiente, >, chequea si el
primer argumento es mayor que el segundo. De otro modo, <= chequea
si el primer argumento es menor o igual al segundo y >= chequea si
el primer argumento es mayor o igual al segundo. En todos los casos,
ambos argumentos deben ser números o marcas (las marcas indican
posiciones en búffers).
= La función = chequea si dos argumentos, ambos números o marcadores,
son iguales.
equal
eq Chequea si dos objetos son el mismo. equal usa un signicado de la
palabra ‘mismo’ y eq usa otro: equal devuelve cierto si los dos objetos
tienen una estructura y contenidos similares, tal como dos copias del
mismo libro. En una mano, eq, devuelve cierto si ambos argumentos
son actualmente el mismo objeto.
string<
string-lessp
string=
string-equal
La función string-lessp chequea si su primer argumento es más pe-
que~
no que el segundo. En resumen, el nombre alternativo para la mis-
ma función (un defalias) es string.
Los argumentos para string-lessp deben ser cadenas o sı́mbolos; la
ordenación es lexicográfica, ası́ el caso es significativo. Los nombres
impresos de sı́mbolos son usado en vez de sı́mbolos por sı́ mismos.
44 Capı́tulo 3: Cómo escribir definiciones de funciones
Una cadena vacı́a, ‘"\"’, es una cadena sin caracteres dentro, es más
peque~na que cualquier cadena de caracteres.
string-equal provee el test correpondiente para igualdad. Su corto
nombre alternativo es string=. No hay funciones test que correponden
a >, >=.
message Imprime un mensaje en el área echo. El primer argumento es una
cadena que puede contener, ‘%s’, ‘%d’, o ‘%c’ para imprimir el valor de
argumentos que siguen la cadena. El argumento usado por ‘%d’ debe
ser un número. El argumento usado por ‘%c’ debe ser un número de
código ascii; eso será impreso como el caracter con este código ascii.
(Otras varias %-secuencias no han sido mencionadas.)
setq
set La función setq asigna el valor de su primer argumento al valor del
segundo argumento. El primer argumento está automáticamente cita-
do por setq. Eso hace lo mismo para los pares de argumentos. Otra
función, set, toma solo dos argumentos y evalúa ambos de ellos an-
tes de configurar el valor devuelto por su primer argumento al valor
devuelto por su segundo argumento.
buffer-name
Sin un argumento, devuelve el nombre del búffer, como una cadena.
buffer-file-name
Sin un argumento, devuelve el nombre del fichero si el búffer lo está
visitando.
current-buffer
Devuelve el búffer en el que Emacs es activo; eso puede no ser el búffer
que es visible en la pantalla.
other-buffer
Devuelve el búffer seleccionado más recientemente (otro que el búffer
pasado a other-buffer como un argumento y otro en vez de el búffer
actual).
switch-to-buffer
Selecciona un búffer para que Emacs esté activo y lo muestre en la
ventana actual y ası́ los usuarios puedan mirarlo. Normalmente se
empareja a C-x b.
set-buffer
Cambia la atención de Emacs a un búffer en el que los programas se
ejecutarán. No altera lo que la ventana está mostrando.
buffer-size
Devuelve el número de caracteres en el búffer actual.
punto Devuelve el valor de la actual posición del cursor, como un entero
contando el número de caracteres desde el principio del búffer.
Sección 3.12: Ejercicios 45
point-min Devuelve el valor mı́nimo permisible del punto en el búffer actual. Esto
es 1, a menos que la contracción esté en efecto
point-max Devuelve el valor del máximo valor permisible del punto en el búffer
actual. Esto es el fin del búffer, a menos que la contracción esté en
efecto
3.12 Ejercicios
• Escribe una función no interactiva que doble el valor de su argumento, un
número. Luego haz la función interactiva.
• Escribe una función que chequee si el valor actual de fill-column es más
grande que el argumento pasado a la función, y si es ası́, imprime un mensaje
apropiado.
46 Capı́tulo 4: Unas pocas funciones de buffer relacionadas
En este capı́tulo estudiamos en detalle varias de las funciones usadas en GNU Emacs.
Esto se llama un “paseo a través”. Estas funciones son usadas como ejemplos de
código Lisp, pero no son ejemplos imaginarios; con la excepción del primero, la
definición de función simplificada, esta funciones muestran el actual código usado
en GNU Emacs. Se puede aprender mucho desde estas definiciones. Las funcio-
nes descritas aquı́ están todas relacionadas a búffers. Después, estudiaremos otras
funciones.
4. La expresión interactive.
5. El cuerpo.
En esta definición de función, la lista de argumentos está vacı́a; esto significa que
esta función no requiere argumentos. (Cuando se busca la definición para la función
completa, se verá que puede pasarse un argumento opcional.)
La expresión interactiva cuenta a Emacs que la función se pretende ser usada
interactivamente. En este ejemplo, interactive no tiene un argumento porque
simplified-beginning-of-buffer no se requiere.
El cuerpo de la función consiste de dos lı́neas:
(push-mark)
(goto-char (point-min))
La primera de estas lı́neas es la expresión, (push-mark). Cuando esta expresión
es evaluado por el intérprete Lisp, eso asigna una marca en la posición actual del
cursor, siempre y cuando esto pueda ser. La posición de esta marca está guardada
en el anillo de marcas.
La siguiente lı́nea es (goto-char (point-min)). Esta expresión salta el cursor
al punto mı́nimo en el búffer, esto es, para el comienzo del búffer (o al principio de
la porción accesible del búffer si eso está encogido. Véase Capı́tulo 6 “Encogiendo
y extendiendo”, página 70.)
El comando push-mark estable una marca en el lugar donde el cursor fué loca-
lizado antes que fuera movido al principio del búffer por la expresión (goto-char
(point-min)). Consiguientemente, puedes, si lo deseas, volver donde estabas origi-
nalmente escribiendo C-x C-x.
¡Esto es todo lo que hay para la definición de función!
Cuando se lee código como este y vuelve a una función no familiar, tal como
goto-char, se puede encontrar que se hace usando el comando describe-function.
Para usar este comando, escribe C-h f y entonces escribe en el nombre de la función
y presiona RET. El comando describe-function imprimirá la documentaciń de la
cadena de la función en una ventana *Help*. Por ejemplo, la documentación para
goto-char es:
Asignar punto a POSITION, un número o marca
Empezando el buffer es la posición (point-min), y el final es
(point-max).
La función es un argumento es la posición deseada.
(La consola para describe-function te ofrecerá el sı́mbolo abajo o precediendo
al cursor, ası́ se puede guardar escribiendo al posicionar el cursor a la derecha o
después de la función y entonces escribir C-h f RET.)
La definición de función end-of-buffer está escrito en el mismo modo que la
definición beginnig-of-buffer excepto que el cuerpo de la función contenga la
expresión (goto-char (point-max)) en lugar de (goto-char (point-min))
Sección 4.3: La definición de mark-whole-buffer 49
escrita ası́ como para parelizar la estructura de la siguiente lı́nea. En cualquier caso,
la lı́nea causa que Emacs determine la posición del punto y asigne una marca allı́.
En las primeras versiones de GNU Emacs, la siguiente lı́nea de mark-whole-
buffer fué (push-mark (point-max)). Esta expresión asigna una marca en el pun-
to en el búffer que tiene el número más alto. Esto será el fin del búffer (o, si el búffer
es encogida, el fin de la porción accesible del búffer. Véase Capı́tulo 6 “Encogien-
do y extendiendo”, página 70, para más acerca de encoger). Después esta marca
ha sido asignada, la marca previa, uno asigna un punto, pero no se asigna largo,
pero Emacs recuerda su posición, solo como todas las otras marcas recientes son
siempre recordadas. Esto significa que se puede, si lo deseas, vuelve a esta posición
escribiendo C-u C-SPC dos veces.
En GNU Emacs 22, el (point-max) es ligeramente más complicado. La lı́nea lee
(push-mark (point-max) nil t)
La expresión funciona cerca de lo mismo que antes. Eso asigna una marca en el
lugar numerado más alto que se puede en el búffer. Sin embargo, en esta versión,
push-mark tiene dos argumentos adicionales. El segundo argumento para push-mark
es nil. Esto cuenta la función que mostrarı́a un mensaje que dice ‘Marca asignada’
cuando eso empuja la marca. El tercer argumento es t. Esto cuenta push-mark
para activar la marca cuando el modo Transient Mark está activado. Transient
Mark mode ilumina la región de marca activa. Con frecuencia desactivada
Finalmente, la última lı́nea de la función es (goto-char (point-min)). Esto
es escrito exactamente el mismo camino camino como está escrito beginning-of-
buffer. La expresión mueve el cursor al mı́nimo punto en el búffer, que es, al
principio del búfferr (o para el principio de la porción accesible del búffer). Como
un resultado de esto, punto está emplazado al principio del búffer y la marca está
asignada al fin del búffer. El búffer completo es, más allá, la región.
(interactive
(list (read-buffer
"Agrega al buffer: "
(other-buffer (current-buffer) t))
(region-beginning)
(region-end)))
Esta expresión no es una con letras separadas por partes, como se describe antes.
En vez de eso, empieza una lista con estas partes:
La primera parte de la lista es una expresión para leer el nombre de un búffer y
lo devuelve como una cadena. Esto es read-buffer. La función requiere una consola
como su primer argumento, ‘"Asocia al buffer: "’. Su segundo argumento cuenta
el comando que valora para proporciona si no especifica cualquier cosa.
En este caso este segundo argumento es una expresión conteniendo la función
other-buffer, una excepción, y una ‘t’, para verdad.
El primer argumento para other-buffer, la excepción, es todavı́a otra función,
other-buffer. Esto es no yendo a ser devuelto. El segundo argumento es el sı́mbolo
para verdad, t. Esto cuenta other-buffer que puede mostrar búffers visibles (ex-
cepto en este caso, eso no mostrará el búffer actual, que tiene sentido).
La expresión se ve como:
(other-buffer (current-buffer) t)
El segundo y tercer argumento de la expresión list son (region-beginning)
y (region-end). Estas dos funciones especifican el principio el y el final del texto
que se adjunta.
Originalmente, el comando usaba las letras ‘B’ y ‘r’. La expresión completa
interactive es ası́:
(interactive "BAsociar al buffer: \nr")
Pero cuando esto fué hecho, el valor por defecto del búffer cambió a ser invisible.
Esto no se querı́a.
(La consola estaba separada del segundo argumento con una nueva lı́nea, ‘\n’.
Estaba seguido por un ‘r’ que contaba a Emacs emparejar los dos argumentos que
siguen el sı́mbolo buffer en la lista de argumentos de la función (que es, start y
end) para los valores de punto y marca. Este argumento trabajó bien.)
1. El sı́mbolo let;
2. Una varlist conteniendo, en este caso, una lista simple de dos elementos, (va-
riable value);
Esta convención formatea que sea fácil de ver que las lı́neas en el cuerpo de
save-excursion, solo como save-excurion por sı́ mismo están encerradas por
los paréntesis asociados con el let:
(let ((oldbuf (current-buffer)))
(save-excursion
...
(set-buffer ...)
(insert-buffer-substring oldbuf start end)
...))
El uso de la función save-excursion puede ser vista como un proceso de rellenar
slots de una plantilla:
(save-excursion
primera-expresion-en-cuerpo
segunda-expresion-en-cuerpo
...
ultima-expresion-en-cuerpo)
En esta función, el cuerpo de save-excursion contiene solo una expresión, la ex-
presión let*. Se conoce una función let. La función let* es diferente. Eso tiene
un ‘*’ en su nombre. Eso permite a Emacs asignar cada variable de su varlist en
secuencia, una después de otra.
Su funcionalidad crı́tica es que las variable después en la varlist puedan hacer
uso de los valores para los que Emacs asigna variables pronto en la varlist. Véase
“fwd-para let”, página 142.
Se obviarán funciones como let* y se focalizará en dos: la función set-buffer
y la función insert-buffer-substring.
En anta~ no, la expresión set-buffer era simple:
(set-buffer (get-buffer-create buffer))
pero ahora eso es
(set-buffer append-to)
append-to se asigna a (get-buffer-create-buffer) pronto en la expresión let*.
Esta asociación extra no serı́a necesaria excepto para este append-to es usado
después en la varlist como un argumento para get-buffer-window-list.
La definición de la función append-to-buffer inserta texto desde el búffer en
el que estás actualmente a un buffer nombrado. Eso sucede que insert-buffer-
substring copia texto desde otro búffer al búffer actual, solo el inverso — que es
porque la definición append-to-buffer empieza con un let que asocia el sı́mbolo
local oldbuf al valor devuelto por current-buffer.
La expresión insert-buffer-substring se ve como esto:
(insert-buffer-substring oldbuf start end)
La función insert-buffer-substring copia una cadena from al búffer especificado
como su primer argumento e inserta la cadena dentro del búffer presente. En este
caso, el argumento para insert-buffer-substring es el valor de la variable creada
y asociada por el let, llama al valor de oldbuf, que fué el búffer actual cuando tu
diste el comando append-to-buffer.
Sección 4.5: Revisar 55
change-back-to-original-buffer-when-finished
let-the-local-meaning-of-oldbuf-disappear-when-finished
En resumen, append-to-buffer funciona como sigue: se guarda el valor del
búffer actual en la variable llamada oldbuf. Se obtiene el nuevo búffer (creando uno
si necesita ser) y cambia la atención de Emacs a eso. Usando el valor de oldbuf,
inserta la región del texto desde el viejo búffer dentro del nuevo búffer; y entonces
usando save-excursion, trae atrás a tu búffer original.
Buscando append-to-buffer, se ha explorado una función limpia compleja.
Eso muestra como usar let y save-excursion, y como cambiar y volver desde otro
buffer. Muchas definiciones de función usan let, save-excursion, y set-buffer
de este modo.
4.5 Revisar
Aquı́ está un breve resumen de varias funciones discutidas en este capı́tulo.
describe-function
describe-variable
Imprime la documentación para una función o variable. Convencional-
mente asociada a C-h f y C-h v.
find-tag Encuentra el fichero que contiene la fuente para una función o varia-
ble y cambia buffer a él, posicionando el punto al principio del ı́tem.
Convencionalmente emparejado a M-. (esto es un perı́odo seguiendo
la tecla META).
save-excursion
Guarda la localización de punto y marca y restaura sus valores des-
pués de los argumentos para save-excursion y han sido evaluados.
También, recuerda el buffer actual y devuélvelo.
insert-buffer-substring
Copia una región de texto desde un búffer que es pasado a la función
como un argumento e inserta la región dentro del búffer actual.
mark-whole-buffer
Marca el búffer completo como una región. Normalmente asignado a
C-x h.
set-buffer
Cambia la atención de Emacs a otro búffer, pero no cambies la ventana
siendo mostrada. Usado cuando el programa en vez de un humano
trabaja en un búffer diferente.
get-buffer-create
get-buffer
Encuentra un búffer nombrado o crea uno si un búffer de este nombre
no existe. La función get-buffer devuelve nil si el nombre del búffer
no existe.
4.6 Ejercicios
• Escribe tu propia definición de función simplified-end-of-buffer; entonces
testea para ver si funciona.
• Usa if y get-buffer para escribir una función que imprime un mensaje con-
tando si un buffer existe.
• Usando find-tag, encuentra la fuente para la función copy-to-buffer
Sección 5.1: La definición de copy-to-buffer 57
5.2.4 El or en el cuerpo
El propósito de la expresión or en la función insert-buffer es asegurar que el
argumento buffer está asociado a un búffer y no solo al nombre de un búffer.
La sección previa muestra como el trabajo podrı́a haber sido hecho usando una
expresión if. Sin embargo, la función insert-buffer actualmente usa or. Para
comprender esto, es necesario comprender como or trabaja.
Una función or puede tener cualquier número de argumentos. Eso evalúa cada
argumento en turno y devuelve el valor del primero de sus argumentos que no es
nil. También, y esto es una funcionalidad crucial de or, eso no evalúa cualquier
argumentos subsiguientes después devolviendo el primer valor no-nil.
La expresión or se ve como esto:
(or (bufferp buffer)
(setq buffer (get-buffer buffer)))
El primer argumento a or es la expresión (bufferp buffer). Esta expresión de-
vuelve cierto (un valor no-nil) si el búffer es actualmente un búffer, y no solo el
nombre de un búffer. En la expresión or, si este es el caso, la expresión or devuelve
esto el valor cierto y no evalúa la siguiente expresión — y esto es bueno para noso-
tros, desde que nosotros no queremos hacer cualquier cosa al valor de buffer si eso
es realmente un búffer.
Por otro lado, si el valor de (bufferp buffer) es nil, que será si el valor de
buffer es el nombre de un buffer, el intérprete Lisp evalúa el siguiente elemento
de la expresión. Esta es la expresión (setq buffer (get-buffer buffer)). Esta
expresión devuelve un valor no-nil, que es el valor para el que asigna la variable
buffer — y este valor es un búffer en sı́, no el nombre de un búffer.
El resultado de todo esto es que el sı́mbolo buffer es siempre asociado a un
búffer en sı́ en vez del nombre de un búffer. Toda es necesario porque la función
set-buffer en una lı́nea siguiente trabaja con un buffer en sı́, no con el nombre de
un búffer.
Incidentalmente, usando or, la situación con el acomodador se verı́a ası́:
(or (holding-on-to-guest) (find-and-take-arm-of-guest))
end se asignadan al principio y al fin del búffer, usando los comandos point-min y
point-max. Note que tenemos aquı́ una ilustración de cómo setq es capaz de asignar
dos variables en la misma expresión. El primer argumento de setq es asignar al valor
del segundo, y su tercer argumento está asignado al valor del cuarto.
Después el cuerpo del save-excursion propio es evaluado, el save-excursion
restaura el búffer original, pero start y end permanece asignado a los valores del
principio y fin del búffer en el que el texto será copiado.
La expresión por fuera save-excursion se ve como:
(save-excursion
(inner-save-excursion-expression
(go-to-new-buffer-and-set-start-and-end)
(insert-buffer-substring buffer start end)
(setq newmark (point)))
La función insert-buffer-substring copia el texto dento del búffer desde la re-
gión indicada por start y end en el búffer. Desde el total del segundo búffer cae
entre start y end, el todo del segundo búffer es copiado dentro del búffer que estás
editando. Lo siguiente, el valor del punto, que será al fin del texto insertado, es
grabado en la variable newmark.
Después el cuerpo de save-excursion es evaluado, punto y marca son recolo-
cados a sus lugares originales.
Sin embargo, es conveniente localizar una marca al fin del texto nuevamente
insertado y localizar el punto al principio. La variable newmark graba el fin del
texto insertado. En la última lı́nea de la expresión let, la expresión de la función
(push-mark newmark) asigna una marca a esta posición. (La posición previa de la
marca está todavı́a accesible; está grabado en la marca del anillo y se puede regresar
a eso con C-u C-SPC.) Mientras tanto, el punto está localizado al principio del texto
insertado, que está donde estaba antes de ser llamado la función que inserta, la
posición de lo que estaba guardado por la primera save-excursion.
La expresión let se parece a esto:
(let (start end newmark)
(save-excursion
(save-excursion
(set-buffer buffer)
(setq start (point-min) end (point-max)))
(insert-buffer-substring buffer start end)
(setq newmark (point)))
(push-mark newmark))
Como la función append-to-buffer, la función insert-buffer usa let,
save-excursion y set-buffer. Además, la función ilustra un camino para usar
o. Toda estas funciones están construyendo el bloque que se encontrarán y usarán
una y otra vez.
nil
excepto, y esto es lo que los novicios confunden, un trabajo muy importante es
hecho dentro de la expresión push-mark.
La función get-buffer devuelve un búffer con el nombre proporcionado. Se
tomará nota de que la función no se llama get-buffer-create; eso no crea un
búffer si uno no existe ya. El búffer devuelto por get-buffer, un búffer existente,
es pasado a insert-buffer-substring, que inserta el total del búffer (desde que
no se especificón ninguna cosa más).
La posición dentro del buffer es insertado es grabado por push-mark. Entonces
la función devuelve nil, el valor de su último comando. Pon otro camino, la función
insert-buffer existe solo para producir un efecto lateral, insertando otro buffer,
no para devolver cualquier valor.
palabra clave &optional, ningún valor necesita ser pasado a este argumento cuando
la función se llama.
La primera lı́nea de la definición de función de beginning-of-buffer tiene lo
siguiente:
(defun beginning-of-buffer (&optional arg)
5.4 Revisar
Aquı́ hay un breve resumen de los asuntos cubierto en este capı́tulo.
or Evalúa cada argumento en secuencia, y devuelve el valor del primer
argumento que no es nil, si ninguno devuelve un valor que no es nil,
devuelve nil. En breve, devuelve el primer valor de verdad de los
argumento; devuelve un valor cierto si un or cualquier de los otros son
verdad.
and Evalúa cada argumento en secuencia, y si cualquiera es nil, devuelve
nil; si ninguno es nil, devuelve el valor del último argumento. En
breve, devuelve un valor cierto solo si todos los argumentos cierto;
devuelve un valor cierto si un and cada uno de los otros son ciertos.
&optional Una palabra clave usaba para indicar que un argumento a una defi-
nición de función es opcional; esto significa que la función puede ser
evaluado sin el argumento, si se desea.
prefix-numeric-value
Convierte el ‘argumento prefijo plano’ producido por (interactive
"P") a un valor numérico.
forward-line
Mueve el punto hacia delante al principio de la siguiente lı́nea, o si
el argumento es más de uno, hacia delante varias lı́neas. Si eso no se
puede mover tan lejos hacia delante como se puede, forward-line va
hacia delante tan lejos como se puede y entonces devuelve un contaje
del número de lı́neas adicionales que no pudo moverse.
Sección 5.5: Ejercicio de argumento opcional 69
erase-buffer
Borra todos los contenidos del búffer actual.
bufferp Devuelve t si su argumento es un búffer; de otro modo devuelve nil.
6 Encogiendo y extendiendo
Encoger es una funcionalidad de Emacs que hace posible focalizar en una parte es-
pecı́fica de un búffer, y funcionar sin cambiar accidentalmente otras partes. Encoger
normalmente se deshabilita puesto que puede confundir a novatos.
Con encoger, el resto del búffer se hace invisible, como si no estuviera. Esto
es una ventaja si, por ejemplo, se quiere reemplazar una palabra en una parte del
búffer pero no otro: se encoge la parte que se quiere y el reemplazo se trae solo en
esta sección, no en el resto del búffer. Las búsquedas solo funcionarán con una región
encogida, no fuera de una, ası́ si tu estás arreglando una parte de un documento,
se puede guardar en sı́ desde encontrar partes accidentalmente que no necesitas
arreglar encongiendo solo la región que quieres. (La tecla asociada a narrow-to-
region es C-x n n.)
Sin embargo, encoger hace que el resto del búffer sea invisible, esto puede asustar
a gente quien inadvertidamente invoca a encoger y pensar que se ha borrado una
parte de su fichero. Más allá, el comando undo (que es normalmente emparejado a C-
x u) no se deja encoger, ası́ las personas pueden llegar a estar bastante desesperadas
si no conocen que ellas pueden volver al resto de un búffer para visibilizarlo con el
comando widen. (El emparejamiento de la tecla para widen es C-x n w.)
Encoger es tan útil al intérprete Lisp como para las personas. Con frecuencia,
una función Emacs Lisp está dise~ nada para trabajar en solo parte de un búffer; o
de manera conversa, una función Emacs Lisp necesita trabajar en todo un búffer
que ha sido encogido. La función what-line, por ejemplo, borra el encogimiento
desde un búffer, si eso tiene cualquier encogimiento y cuando eso ha finalizado su
trabajo, restaura el encogimiento que haya. Por otro lado, la función count-lines,
que es llamada por what-line, usa el encogimiento para restringirse a sı́ misma solo
a la porción del búffer en el que se está interesado y entonces restaura la situación
previa.
6.2 what-line
El comando what-line cuenta el número de la lı́nea en la que el cursor se ha localiza-
do. La función ilustra el uso de los comandos save-restriction y save-excursion.
Aquı́ está el texto original de la función:
(defun what-line ()
"Imprime el número de lı́nea actual (en el búffer)
del punto."
(interactive)
(save-restriction
(widen)
(save-excursion
(beginning-of-line)
(message "Lı́nea %d"
(1+ (count-lines 1 (point)))))))
(En versiones recientes de GNU Emacs, la función what-line se ha expandido
para contar el número de lı́neas en un búffer encogido tan bien como el número
de lı́neas en un búffer ampliado. La versión reciente es más compleja que la ver-
sión que se muestra. Alguien sintiéndose venturoso, podrı́a querer mirarla después
de entender como esta versión funciona. Probablemente se necesitará usar C-h f
(describe-function). La versión nueva usa un condicional para determinar si el
búffer se ha encogido.
(También, eso usa line-number-at-pos, que otras expresiones simples, tales
como (goto-char (point-min)), mueve el punto al principio de la lı́nea actual con
(forward-line 0) en vez de beginning-of-line.)
72 Capı́tulo 6: Encogiendo y extendiendo
En Lisp, car, cdr, y cons son funciones fundamentales. La función cons es usada
para construir listas, y las funciones car y cdr son usadas para tomarlas aparte.
En el paseo guiado a través de la función copy-region-as-kill, se verá cons
tan bien como dos variantes de cdr, llamadas setcdr y nthcdr. (Véase Sección 8.3
“copy-region-as-kill”, página 90.)
El nombre de la función cons es razonable: es una abreviación de la palabra
‘constructo’. Los orı́genes de los nombres car y cdr, por otro lado, son esotéricos:
car es un acrónimo de la frase ‘Contenidos de la parte de la Dirección del Registro’;
y cdr (pronunciado ‘could-er’) es un acrónimo de la frase ‘Contenidos del Decre-
mento del Registro’. Estas frases se refieren a piezas especı́ficas de hardware en el
ordenador en el que el Lisp original fué desarrollado. El resto de frases han sido
completamente irrelevantes por más de 25 a~ nos a cualquiera pensando acerca de
Lisp. Ninguno, excepto unos pocos académicos valientes han empezado a usar nom-
bres más razonables para estas funciones, los viejos términos están todavı́a en uso.
En particular, los términos usados en el código fuente Emacs Lisp, que usaremos en
esta introducción.
(Hay una lección aquı́: cuando se nombran nuevas funciones, considere muy
cuidadosamente lo que se está haciendo, ya que puede estar pegadas con los nombres
largos que se esperan. La razón es que este documento perpetúa estos nombres que
el código fuente de Emacs Lisp usa, y si no los usaba, se estarı́a un duro rato leyendo
el código; pero, por favor, se debe intentar evitar usar estos términos por uno mismo.
Las personas que vengan después se lo agradecerán.
Cuando car y cdr se aplica a una lista hecha de sı́mbolos, tal como la lista
(pino roble abeto arce), el elemento de la lista devuelta por la función car es
el sı́mbolo pino sin cualquier paréntesis alrededor. pino es el primer elemento en
la lista. Sin embargo, el cdr de la lista es una lista por sı́ misma, (roble abeto
arce), como se puede ver evaluando las siguientes expresiones en el modo usual:
(car ’(pino roble abeto arce))
7.2 cons
La función cons construye listas; eso es lo inverso de car y cdr. Por ejemplo, cons
puede usarse para crear una lista de cuatro elementos desde los tres elementos de
la lista, (abeto roble arce):
(cons ’pino ’(abeto roble arce))
76 Capı́tulo 7: car, cdr, cons: Funciones fundamentales
1
Actualmente, se puede cons un elemento para un átomo para producir a para punteado.
Los pares punteados no se discuten aquı́; ver Sección “Notación de Para Punteado” in
El Manual de Referencia de GNU Emacs Lisp.
Sección 7.3: nthcdr 77
7.3 nthcdr
La función nthcdr está asociada con la función cdr. Que lo que hace es tomar la
cdr de una lista repetidamente.
Si se toma el cdr de la lista (pino roble abeto arce), será devuelta la lista
(roble abeto arce). Si se repite esto en lo que se devolvió, se devolverá lista lista
(abeto arce). (De acuerdo, se repite cdr en la lista original solo se dará cdr desde
la función que no cambia la lista. Se necesita evaluar el cdr del cdr y ası́.) Si se
contiúa esto, finalmente será devuelta una lista vacı́a, que en este caso, en vez de
ser mostrada como () es mostrado como nil.
Para revisar, aquı́ hay una serie de cdrs repetidos, el siguiente ‘⇒’ muestra lo
que se devuelve.
(cdr ’(pino roble abeto arce))
⇒(roble abeto arce)
(cdr ’(arce))
⇒ nil
(cdr ’nil)
⇒ nil
78 Capı́tulo 7: car, cdr, cons: Funciones fundamentales
(cdr ())
⇒ nil
También se pueden hacer varios cdrs sin imprimir los valores ası́:
(cdr (cdr ’(pino roble abeto arce)))
⇒ (abeto arce)
En este ejemplo, el intérprete Lisp evalúa la lista propia dentro. La lista de dentro
está entre comillas, ası́ solo pasa la lista del cdr más interno. Este cdr pasa una
lista hecho del segundo y subsiguientes elementos de la lista al cdr más externo,
que produce una lista compuesta del tercer y subsiguientes elementos de la lista
original. En este ejemplo, la función cdr se repite y devuelve una lista que consiste
de la lista original sin sus primeros dos elementos.
La función nthcdr hace lo mismo que repitiendo la llamada a cdr. En el siguiente
ejemplo, el segundo argumento se pasa a la función nthcdr, a través con la lista,
y el valor devuelto es la lista sin sus primeros dos ı́tems, que son exactamente los
mismos dos cdr en la lista:
(nthcdr 2 ’(pino roble abeto arce))
⇒ (abeto arce)
Usando la lista original de cuatro elementos, se puede ver qué ocurre cuando
varios argumentos numéricos son pasados a nthcdr, incluyendo 0, 1, y 5:
;; Deja la lista como estaba.
(nthcdr 0 ’(pine fir oak maple))
⇒ (pino roble abeto arce)
7.4 nth
La función nthcdr toma el cdr de una lista repetidamente. La función nth toma
el car del resultado devuelto por nthcdr. Devuelve el elemento Nth de la lista.
Sección 7.5: setcar 79
7.5 setcar
Como se podrı́a adivinar desde sus nombres, las funciones setcar y setcdr asignan
el car o la cdr de una lista a un nuevo valor. Ellos actualmente cambia la lista
original, no como car y cdr que deja la lista original como estaba. Un camino para
encontrar cómo esto funciona es experimentar. Se comenzará con la función setcar.
Primero, podemos crear una lista y entonces asignar el valor de una variable a
la lista usando la función setq. Aquı́ hay una lista de animales:
(setq animales ’(antilope jirafa leon tigre))
Si está leyendo esto en Info dentro de GNU Emacs, se puede evaluar esta expresión
del modo usual, posicionando el cursor después de la expresión y escribiendo C-
x C-e. Esto es una de las ventajas de tener el intérprete construido dentro del
entorno de computación. Incidentalmente, cuando no hay nada en la lı́nea después
del paréntesis final, tal como un comentario, el punto puede estar en la siguiente
lı́nea. De este modo, si tu cursor está en la primera columna de la siguiente lı́nea,
no se necesita mover. En realidad, Emacs permite cualquier cantidad de espacio en
blanco después del paréntesis final.)
80 Capı́tulo 7: car, cdr, cons: Funciones fundamentales
7.6 setcdr
La función setcdr es similar a la función setcar, excepto que la función reemplaza
el segundo y subsiguientes elementos de una lista en vez del primer elemento.
(Para ver cómo se cambia el último elemento de una lista, mira hacia delante a
la “La función kill-new”, página 94, que usa las funciones nthcdr y setcdr.)
Para ver cómo esto funciona, asigna el valor de la variable a una lista de animales
domesticados evaluando la siguiente expresión:
(setq animales-domesticos ’(caballo vaca oveja cabra))
Si ahora se evalúa la lista, lo que se devuelve es (caballo vaca oveja cabra):
animales-domesticos
⇒ (caballo vaca oveja cabra)
Lo siguiente, evalúa setcdr con dos argumentos, el nombre de la variable que
tiene una lista como su valor, y la lista para la que el cdr de la primera lista sea
asignada;
(setcdr animales-domesticos ’(gato perro))
Si se evalúa esta expresión, la lista (gato perro) aparecerá en el área echo. Este
es el valor devuelto por la función. El resultado en el que estamos interesados es el
“efecto lateral”, que se puede ver evaluando la variable domesticated-animals:
animales-domesticos
⇒ (caballo gato perro)
En realidad, la lista es cambiada desde (caballo vaca oveja cabra) a (caballo
gato perro). El cdr de la lista es cambiada desde (vaca oveja cabra) a (gato
perro).
Sección 7.7: Ejercicio 81
7.7 Ejercicio
Construye una lista de cuatro pájaros evaluando varias expresiones con cons. En-
cuentra que ocurre cuando cons una lista dentro de sı́. Reemplaza el primer elemento
de la lista de cuatro pájaros con un pez. Reemplaza el resto de esta lista con una
lista de otro pez.
82 Capı́tulo 8: Cortando y almacenando texto
8.1 zap-to-char
La función zap-to-char cambió poco entre GNU Emacs versión 19 y GNU Emacs
versión 22. Sin embargo, zap-to-char llama a otra función, kill-region, que se
reescribió más.
La función kill-region en Emacs 19 es compleja, pero no usa código que es
importante en este momento. Se obviará.
La función kill-region en Emacs 22 es más de fácil leer que la misma función
en Emacs 19 e introduce un concepto muy importante, que el error maneja. Nosotros
pasearemos a través de la función.
Pero primero, déjanos ver en la función interactive zap-to-char.
La función zap-to-char elimina el texto en la región entre la localización del
curso (por ej. punto) para incluir la siguiente ocurrencia de un caracter especı́fico.
El texto que zap-to-char borra es puesto en el kill ring anillo de la muerte; y
puede ser recuperado desde el kill ring anillo de la muerte escribiendo C-y (yank).
Si el comando dado es un argumento, eso borra texto a través de este número
de ocurrencias. De este modo, si el cursor estuviera al principio de esta frase y el
carácter fuera ‘s’, ‘De este modo’ serı́a borrado. Si el argumento fueran dos, ‘De
este modo, si el cursor’ se borrase, y incluirı́a la ‘s’ en el ‘cursor’.
Si el carácter especı́fico no encuentra zap-to-char dirá “Búsqueda fallida”, eso
cuenta el carácter que se escribió, y no eliminó cualquier texto.
En orden para determinar cuánto texto eliminar zap-to-char usa una función
de búsqueda. Las búsquedas son usadas extensivamente en el código que manipula
texto, y focalizará la atención en ellos tan bien como el comando de borrado.
Aquı́ está el texto completo de la versión 22 de la función:
(defun zap-to-char (arg char)
"Corta e incluye ARG’th ocurrencia de CHAR
En caso de ser ignorada si ‘case-fold-search’ es no nulo en el
búffer actual.
Para ir atrás si ARG es negativo; error si CHAR no se encuentra."
(interactive "p\ncZap to char: ")
(if (char-table-p translation-table-for-input)
(setq char (or (aref translation-table-for-input char) char)))
(kill-region (point) (progn
(search-forward (char-to-string char)
nil nil arg)
(point))))
La documentación es en lı́nea. Se necesita conocer el significado de la jerga de
la palabra ‘kill’.
8.2 kill-region
La función zap-to-char usa la función kill-region. Esta función corta texto desde
una región y copia este texto al kill ring anillo de la muerte, desde el que puede ser
recuperado.
Tanto la versión de Emacs 22 de esta función usa condition-case y
copy-region-as-kill, ambas se explicarán. condition-case es una forma
especial importante.
En esencia, la función kill-region llama a condition-case, que toma tres
argumentos. En esta función, el primer argumento no hace nada. El segundo argu-
mento contiene el código hace el trabajo cuando todo va bien. El tercer argumento
contiene el código que se llama en el evento de un error.
Sección 8.2: kill-region 87
(if kill-read-only-ok
(progn (message "Lee solo texto copiado para el kill ring") nil)
(barf-if-buffer-read-only)
;; If the buffer isn’t read-only, the text is.
(signal ’text-read-only (list (current-buffer)))))
8.2.1 condition-case
Como se ha visto antes (véase Sección 1.3 “Genera un Mensaje de Error”, página 4),
cuando el intérprete Emacs Lisp tiene problemas evaluando una expresión, se provee
de una ayuda; en jerga, se dice “signaling an error” se~nalando un error. Normal-
mente, el ordenador para el programa y te muestra un mensaje.
Sin embargo, algunos programas garantizan acciones complicadas. Eso no pa-
rarı́a en un error. En la función kill-region, la mayorı́a parece un error que in-
tentará cortar texto que es de solo lectura y no puede ser eliminado. Ası́ la fun-
ción kill-region contiene código para manejar esta circunstancia. Este código,
hace que el cuerpo de la función kill-region, esté dentro de una forma especial
condition-case.
La plantilla para condition-case se parece a esto:
(condition-case
var
bodyform
error-handler...)
Sección 8.2: kill-region 89
8.3 copy-region-as-kill
La función copy-region-as-kill copia una región de texto desde un búffer y (via
kill-append o kill-new) lo guarda en el kill-ring.
Si se llama a copy-region-as-kill inmediatamente después de un comando
kill-region, Emacs inserta el texto nuevamente copiado al texto copiado previa-
mente. Esto significa que si se pega el texto, se obtiene todo, tanto esto, como la
operación previa. Por otro lado, si algún otro comando precede la copy-region-
as-kill, la función copia el texto dentro de una entrada separada el kill ring anillo
de la muerte.
Aquı́ está el texto completo de la versión 22 de la función copy-region-as-kill:
(defun copy-region-as-kill (beg end)
"Guarda la región como si estuviera cortada, pero no la cortes.
En el modo Marca de Tránsito Transient Mark, se desactiva la
marca.
Si ‘interprogram-cut-function’ es no nulo, también se guarda el
texto para una sistema de ventanas de cortar y pegar."
(interactive "r")
(if (eq last-command ’kill-region)
(kill-append (filter-buffer-substring beg end) (< end beg))
(kill-new (filter-buffer-substring beg end)))
(if transient-mark-mode
(setq deactivate-mark t))
nil)
Sección 8.3: copy-region-as-kill 91
La función kill-append
La función kill-new se ve como ası́:
(defun kill-append (string before-p &optional yank-handler)
"Inserta STRING al fin del último corte en el anillo de la muerte kill ring.
Si BEFORE-P es no nulo, inserta STRING.
... "
(let* ((cur (car kill-ring)))
(kill-new (if before-p (concat string cur) (concat cur string))
(or (= (length cur) 0)
(equal yank-handler
(get-text-property 0 ’yank-handler cur)))
yank-handler)))
La función kill-append es limpia. Usa la función kill-new, que discutiremos en
más detalle en un momento.
(También, la función provee un argumento opcional llamado yank-handler;
cuando se invoque, este argumento cuenta a la función cómo tratar con la propie-
dades a~nadidas al texto, tales como ‘negrilla’ o ‘itálicas’.)
Sección 8.3: copy-region-as-kill 93
Eso tiene una función let* para asignar el valor del primer elemento del kill
ring a cur. (No se sabe por qué la función no usa let; solo un valor es asignado en
la expresión. ¿Quizás esto es un error que no produce problemas?
Considera el condicional que es uno de los dos argumentos para kill-new. Eso
usa concat para concatenar el nuevo texto al car del anillo de la muerte kill ring.
Si eso se concatena atrás o delante depende de los resultados de una expresión if:
(if before-p ; if-part
(concat string cur) ; then-part
(concat cur string)) ; else-part
Si la región cortada está antes que la región que se cortó en el último comando,
entonces deberı́a ser puesto antes que el material salvador en el anterior corte kill ; y
de manera contraria, si el texto cortado sigue lo que fué cortado, eso serı́a a~nadido
después del texto previo. La expresión if depende del predicado before-p para
decidir si el texto nuevamente salvado es puesto antes o después.
El sı́mbolo before-p es el nombre de uno de los argumentos a kill-append.
Cuando la función kill-append se evalúa, se asocia al valor devuelto evaluando el
argumento actual. En este caso, esta es la expresión (< end beg). Esta expresión
no determina directamente si el texto cortado en este comando se localiza antes
o después del texto cortado del último comando; lo que hace es determinar si el
valor de la variable end es menor que el valor de la variable beg. Si es ası́, significa
que el usuario se encara al principio del búffer. También, el resultado de evaluar la
expresión del predicado. (< end beg), será verdadero y el texto se concatena antes
del texto previo. Por otro lado, si el valor de la variable end es mayor que el valor
del la variable beg, el texto será concatenado después del texto previo.
Cuando el texto nuevamente guardado se concatena, entonces la cadena con el
nuevo texto será concatenado antes del viejo texto:
(concat string cur)
Pero si el texto será a~
nadido, eso será concatenado después del viejo texto:
(concat cur string))
Para comprender cómo funciona esto, primero se necesita revisar la función
concat. La función concat enlaza junto o une dos cadenas de texto. El resultado
es una cadena. Por ejemplo:
(concat "abc" "def")
⇒ "abcdef"
(concat (car
’("primer elemento" "segundo elemento")) " modificado")
⇒ "primer elemento modificado"
Ahora puede tener sentido kill-append: eso modifica los contenidos del anillo
de la muerte kill ring. El anillo de la muerte kill ring es una lista, en la que cada
elemento es texto guardado. La función kill-append usa la función kill-new que
usa la función setcar.
94 Capı́tulo 8: Cortando y almacenando texto
La función kill-new
La función kill-new se ve de esta manera:
(defun kill-new (string &optional replace yank-handler)
"Crea STRING el último corte en el anillo de la muerte kill
ring.
Asigna ‘kill-ring-yank-pointer’ para apuntarlo.
Ahora para la segunda parte de la claúsula if. Esta expresión deja el kill ring
desde lo creciente demasiado largo. Eso se ve de la siguiente manera:
(if (> (length kill-ring) kill-ring-max)
(setcdr (nthcdr (1- kill-ring-max) kill-ring) nil))
El código chequea si el tama~no del anillo de la muerte kill ring es más grande
que el máximo tama~ no permitido. Este es el valor de kill-ring-max (que es 60, por
defecto). Si el tama~
no del anillo de la muerte kill ring es demasiado largo, entonces
este código asigna el último elemento del anillo de la muerte kill ring a nil. Eso
hace esto usando dos funciones, nthcdr y setcdr.
Nosotros vemos que setcdr temprano (véase Sección 7.6 “setcdr”, página 80).
Eso asigna el cdr de una lista, solo como setcar asigna el car de una lista. En este
caso, sin embargo, setcdr no estará configurando el cdr del kill ring completo; la
función nthcdr es usada para causarlo para asignar el cdr del siguiente al último
elemento del kill ring — esto significa que desde el cdr del siguiente al último
elemnto del kill ring anillo de la muerte, eso asignará el último elemento del kill
ring anillo de la muerte.
La función nthcdr funciona repetidamente tomando el cdr de una lista — eso
toma el cdr del cdr del cdr . . . . Eso hace esto N veces y devuelve los resultados.
(Véase Sección 7.3 “nthcdr”, página 77.)
De este modo, si teniamos una lista de cuatro elemento que era supuestamente
de tres elementos, se podrı́a asignar el cdr del siguiente al último elemento a nil,
y por eso se ordena la lista. (Si se asigna el último elemento a algún otro valor a
nil, que se podrı́a hacer, entonces no se habrı́a ordenado la lista. Véase Sección 7.6
“setcdr”, página 80.)
Se puede ver ordenando la evaluación de las siguientes tres expresiones en turno.
Primero asigna el valor de arboles a (arce encina pino abedul) entonces asigna
el cdr de su segundo cdr y entonces encuentra el valor de arboles.
(setq arboles ’(arce encina pino abedul))
⇒ (arce encina pino abedul)
árboles
⇒ (arce encina pino)
(El valor devuelto por la expresión setcdr es nil desde que es que el cdr es
asignado.)
Para repetir, en kill-new, la función nthcdr toma el cdr un número de veces
que es uno menos que el tama~no máximo permitido del anillo de la muerte kill ring
y setcdr asigna el cdr de este elemento (que será el resto de los elementos en el
anillo muerte) para nil. Esto previene el anillo de la muerte kill ring desde lo que
crece demasiado largo.
Sección 8.3: copy-region-as-kill 97
Estamos yendo a discutir sistemas de ventanas y otros programas más allá pero
meramente nota que este es un mecanismo que habilita GNU Emacs a trabajar
fácilmente y bien con otros programas.
Este código para emplazar texot en el anillo de la muerte kill ring, concatenado
con un elemento existente o como un nuevo elemento, nos lidera al código para traer
texto que ha sido cortado del búffer — los comandos de corte. Sin embargo, antes de
discutir los comandos de corte, es mejor aprender cómo las listas son implementadas
en un ordenador. Esto dejará claro tales misterios como el uso del término ‘puntero’.
Pero antes de esto, nos desviaremos a C.
1
Más precisamente, y requiriendo conocimiento más experto para comprender, los dos
enteros son del tipo ‘Lisp Object’, que puede también ser una unión C en vez de un
tipo de entero.
100 Capı́tulo 8: Cortando y almacenando texto
Desde el punto de vista de la persona que escribe Lisp, Emacs es muy simple;
pero oculta en el fondo un gran trato de complejidad para hacer todo el trabajo.
8.6 Revisar
Aquı́ hay un breve resumen de algunas funciones introducidas recientemente.
car
cdr car devuelve el primer elemento de una lista; cdr devuelve el segundo
y subsiguientes elementos de una lista.
Por ejemplo:
(car ’(1 2 3 4 5 6 7))
⇒ 1
(cdr ’(1 2 3 4 5 6 7))
⇒ (2 3 4 5 6 7)
Por ejemplo:
(cons 1 ’(2 3 4))
⇒ (1 2 3 4)
funcall funcall evalúa su primer argumento como una función. Ası́ pasa los
argumentos que permanecen a su primer argumento.
nthcdr Devuelve el resultado de tomar cdr ‘n’ veces en una lista. The nth
cdr. El ‘resto del resto’, como estaba
Por ejemplo:
(nthcdr 3 ’(1 2 3 4 5 6 7))
⇒ (4 5 6 7)
setcar
setcdr setcar cambia el primer elemento de una lista; setcdr cambia el
segundo y subsiguiente elementos de una lista.
Por ejemplo:
(setq triple ’(1 2 3))
triple
⇒ (37 2 3)
triple
⇒ (37 "foo" "bar")
save-restriction
Graba siempre que encoger esté en efecto en el búffer, si cualquiera,
restaura este encogimiento después de evaluar los argumentos.
search-forward
Buscar una cadena, y si la cadena es encontrada, mueve el punto. Con
una expresión regular, usa algo similar a re-search-forward. (Véase
Capı́tulo 12 “Buscar regexp”, página 135, para una explicación de
expresiones regulares patrones y búsquedas.)
Sección 8.7: Buscando ejercicios 103
nil
En el diagrama, cada caja representa una palabra de memoria del ordenador que
maneja un objeto Lisp, normalmente en la forma de una dirección de memoria.
Las cajas, por ej. las direcciones, están en pares. Cada flecha apunta a lo que la
dirección es la dirección de, si un átomo u otro par de direcciones. La primera caja
es la dirección electrónica de ‘rosa’ y la flecha apunta a ‘rosa’; la segunda caja es
la dirección del siguiente par de cajas, la primera parte de la que es la dirección
de ‘violeta’ y la segunda parte es la dirección del siguiente par. La última caja
apunta al sı́mbolo nil, que marca el fin de la lista.
Cuando una variable es configurado a una lista con una función tal como setq,
almacena la dirección de la primera caja en la variable. De este modo, la evaluación
de la expresión es:
(setq ramo ’(rosa violeta botóndeoro))
105
bouquet
nil
En este ejemplo, el sı́mbolo ramo maneja la dirección del primer par de cajas.
Esta misma lista puede ser ilustrada en un modo diferente de anotación de cajas
como esta:
bouquet
produce esto:
bouquet flowers
nil
bouquet flowers
nil
buttercup
lily violet
rose
Sin embargo, esto no cambia el valor del sı́mbolo flores, ası́ puedes ver evaluando
lo siguiente,
(eq (cdr (cdr ramo)) flores)
que devuelve t para verdad.
Hasta que se resetea, flores todavı́a tiene el valor de (violeta botóndeoro);
que es, eso tiene la dirección de la celula cons cuya primera dirección es violeta.
También, esto no altera cualquier célula prexistente cons; ellas está todavı́a allı́.
De este modo, en Lisp, tiene el cdr de una lista, se obtiene la dirección del
siguiente cons en las serie; para tener el car de una lista, se obtiene la dirección del
primer elemento de la lista; para cons un nuevo elemento en una lista, se a~ nade una
nueva célula cons al frente de la lista. ¡Esto es todo lo que hay ası́! ¡La estructura
subyacente de Lisp es brillantemente simple!
¿Y qué hace la última dirección en una serie de células cons se refieren? Eso es
la dirección de la lista vacı́a, de nil.
En resumen, cuando una variable Lisp es asignada a un valor, eso provee con la
dirección de la lista a la que la variable se refiere.
directions to map to
symbol name bouquet
directions to
symbol definition [none]
directions to map to
variable name (rose violet buttercup)
directions to
property list [not described here]
9.2 Ejercicio
Asignar flores a violeta y botóndeoro. Asigna dos flores más en esta lista y
asigna esta nueva lista a mas-flores. Asigna el car de flores a un pez. ¿Qué lista
contiene ahora mas-flores?
108 Capı́tulo 10: Pegando texto
10 Pegando texto
Siempre y cuando se corta texto fuera de un búffer con un comando ‘kill’ en GNU
Emacs, se puede traer con un comando ‘pegar’. El texto cortado del búffer es pues-
to en el anillo de la muerte y en los comandos pegar, se insertan los contenidos
apropiados del kill ring detrás de un búffer (no necesariamente el búffer original).
Un simple comando C-y (yank) inserta el primer ı́tem desde el anillo de la muer-
te kill ring dentro del actual búffer. Si el comando C-y es seguido inmediatamente
para M-y, el primer elemento se reemplaza por el segundo elemento. Los sucesivos
comandos M-y reemplazan el segundo elemento con el tercer, cuarto, o quinto ele-
mento, y ası́. Cuando se llega al último elemento en el anillo de la muerte kill ring,
se reemplaza por el primer elemento y el ciclo se repite. (De este modo, el kill ring
se llama un ‘anillo’ en vez de solo una ‘lista’. Sin embargo, la estructura de de datos
actual que maneja el texto es una lista. Véase hundefinedi “Manejando el anillo
de la muerte kill ring”, página hundefinedi, para los detalles de cómo la lista es
manejada como un anillo.)
kill-ring kill-ring-yank-pointer
nil
yet more text
a different piece of text
some text
Estas dos maneras hablar acerca de la misma cosa suena confuso al principio
pero tiene sentido para reflexionar. El kill ring anillo de la muerte es generalmente
pensado como la estructura completa de datos que manejan la información de lo que
se ha cortado reciéntemente de los búffers de Emacs. El kill-ring-yank-pointer
en la otra mano, sirve para indicar — que es, para ‘apuntar a’ — esta parte del
anillo de la muerte del que el primer elemento (el car) será insertado.
110 Capı́tulo 10: Pegando texto
11 Bucles y recursión
Emacs Lisp tiene dos caminos primarios para causar una expresión, o una serie de
expresiones, para ser evaluado repetidamente: uno usa un bucle while, y el otro usa
recursión.
La repetición puede ser valorable. Por ejemplo, para mover hacia delante cuatro
frases, tu solo necesitas escribir un programa que moverá hacia delante una frase
y entonces repite el proceso cuatro veces. Ya que un ordenador no está aburrido
o cansado, tal acción repetitiva no tiene los efectos de borrado por equivocación o
exceso que pueden tener los humanos.
La gente mayoritariamente escribe funciones de Emacs Lisp usando bucles
while; pero se puede usar recursión, que provee un poderoso camino mental pa-
ra resolver problemas1 .
11.1 while
La forma especial while chequea si el valor devuelto para evaluar el primer argu-
mento es verdadero o falso. Esto es parecido a lo que el intérprete Lisp hace con un
if; el intérprete hace lo siguiente, sin embargo, es diferente.
En una expresión while, si el valor devuelto por evaluar el primer argumento es
falso, el intérprete Lisp descarta el resto de la expresión (el cuerpo de la expresión)
y no la evalúa. Sin embargo, si el valor es cierto, el intérprete Lisp evalúa el cuerpo
de la expresión y entonces de nuevo chequea si el primer argumento para while es
cierto o falso. Si el valor devuelto de evaluar el primer argumento es cierto de nuevo,
el intérprete Lisp evalúa el cuerpo de la expresión.
La plantilla para una expresión while se ve ası́:
(while test-verdadero-o-falso
cuerpo...)
En el momento en el que el true-or-false-test de la expresión while devuelve un
valor cierto cuando eso se evalúa, el cuerpo es repetidamente evaluado. Este proceso
se llama bucle puesto que el intérprete Lisp repite la misma cosa una y otra vez,
como un avión haciendo un loop. Cuando el resultado de evaluar el true-or-false-test
es falso, el intérprete Lisp no evalúa el resto de la expresión while y ‘existe el bucle’.
Claramente, si el valor devuelto evaluando el primer argumento para while es
siempre cierto, el cuerpo siguiente será evaluado una y otra vez . . . y . . . para
siempre. Recı́procamente, si el valor devuelto nunca es cierto, las expresiones en
el cuerpo nunca serán evaluadas. La fortaleza de escribir un bucle while consiste
de elegir un mecanismo tal que el true-or-false-test devuelva cierto solo el número
1
Se pueden escribir funciones recursivas para ser frugal o basura mental o recursos de
ordenador; como eso ocurre, los métodos que la gente encuentra fáciles — son fruga-
les de ‘recursos mentales’ — algunas veces usan recursos de ordenador considerables.
Emacs fué dise~nado para ejecutarse en máquinas que ahora se consideran limitadas
y sus configuraciones por defecto son conservadoras. Se puede querer incrementar los
valores de max-specdl-size y max-lisp-eval-depth. En mi fichero .emacs, yo los
asigno a 15 o 30 veces su valor por defecto.
112 Capı́tulo 11: Bucles y recursión
de veces que requieren las subsiguientes expresiones para ser evaluadas, y entonces
tener el test devuelto a falso.
El valor devuelto evaluando while es el valor del true-or-false-test. Una conse-
cuencia interesante de esto es que un bucle while que evalúa sin errores devolverá
nil o falso sin dignidad de si eso ha girado 1 o 100 veces o ninguna. ¡Una expresión
while que se evalúa de manera exitosa nunca devuelve un valor cierto! Lo que esto
significa es que while es siempre evaluado por sus efectos laterales, que es decir, las
consecuencias de evaluar las expresiones con el cuerpo del bucle while. Esto tiene
sentido. Eso no es el mero acto del bucle que es deseado, pero las consecuencias de
lo que ocurre cuando las expresiones en el bucle son repetidamente evaluadas.
animales
De este modo, para un bucle while que chequea si hay cualquier ı́tem en la lista
animales, la primera parte del bucle será escrito ası́:
(while animales
...
Cuando el while chequea su primer argumento, la variable animales se evalúa.
Eso devuelve una lista. Mientras la lista tiene elementos, el while considera los
resultados del test para ser verdadero; pero cuando la lista es vacı́a, eso considera
los resultados del test para ser falso.
Para prevenir que el bucle while se ejecute siempre, se necesita proporcionar
algún mecanismo. Una técnica usada con frecuencia es tener una de las subsiguientes
Sección 11.1: while 113
formas en la expresión while que asigna el valor de la lista para ser el cdr de la
lista. Cada vez que la función cdr se evalúa, se va reduciendo, hasta que finalmente
solo queda la lista vacı́a. En este punto, el test del bucle while devolverá falso, y
los argumentos para el while no se evaluarán.
Por ejemplo, la lista de animales asociada a la variable animals se puede asignar
a ser el cdr de la lista original con la siguiente expresión:
(setq animals (cdr animals))
Si se han evaluado las expresiones previas y entonces se evalúa esta expresión, se
verá (jirafa leon tigre) que aparecerá en el área echo. Si se evalúa la expresiń
de nuevo, (leon tigre) aparecerá en el área echo. Si se evalúa de nuevo, (tigre)
y todavı́a de nuevo aparecerá la lista vacı́a y se mostrará como nil.
Una plantilla para un bucle while usa la función cdr repetidamente para causar
el true-or-false-test finalmente para chequear la veracidad y se parece a esto:
(while test-whether-list-is-empty
body...
set-list-to-cdr-of-list)
Este chequeo y uso de cdr puede ser puesto junto a una función que va a través
de una lista e imprime cada elemento de la lista en una lı́nea de sı́ misma.
(imprimir-elementos-de-la-lista animales)
Cuando se evalúan las tres expresiones en secuencia, se verá esto:
gacela
jirafa
leon
tigre
nil
Cada elemento de la lista se imprime en una lı́nea en sı́ (que es lo que la función
print hace) y entonces el valor devuelto por la función se imprime. Desde que la
última expresión en la función es el bucle while, y desde que el bucle while siempre
devuelve nil, un nil se imprime después del último elemento de la lista.
•
• •
• • •
• • • •
(triangle 7)
La suma del primero de cuatro números es 10 y la suma de los primeros siete
números es 28.
es 6, y entonces a~ nade el total de estas dos filas a la fila esta que lo precede, que es
5, y ası́. Como en el ejemplo previo, cada adición solo involucra la adición de dos
números, el total de las filas ya se a~ nadió y el número de asteriscos en la fila que
está siendo a~nadida al total. Este proceso de a~ nadir dos números se repite de nuevo
y de nuevo hasta que no haya más asteriscos que a~ nadir.
Sabemos con cuántos asteriscos empezar: el número de asteriscos en la última fila
es igual al número de filas. Si el triángulo tiene siete filas, el número de asteriscos en
la última fila es 7. Más allá, sabemos cuántos asteriscos están en la fila precedente:
eso es uno menos que el número en la fila.
La macro dolist
Supón, por ejemplo, que quieres invertir una lista, ası́ que “primero”, “segundo”,
“tercero” llega a ser “tercero”, “segundo”, “primero”.
En la práctica, usarı́as la función reverse, como esta:
(setq animales ’(gacela jirafa leon tigre))
(reverse animales)
Aquı́ se ve cómo se podrı́a invertir la lista usando un bucle while:
(setq animales ’(gacela jirafa leon tigre))
(reverse-list-with-while animales)
Y aquı́ se ve cómo podrı́a usarse la macro dolist:
(setq animales ’(gacela jirafa leon tigre))
(reverse-list-with-dolist animals)
En Info, se puede localizar su cursor después de cerrar paréntesis de cada expresión
y escribir C-x C-e; en cada caso, se verı́a
(tigre leon jirafa gacela)
en el área echo.
Para este ejemplo, la función reverse existente es obviamente la mejor. El bucle
while es solo como nuestro primer ejemplo (véase Sección 11.1.1 “Un bucle while
y una lista”, página 112). El while primero chequea si la lista tiene elementos; si
es ası́, eso construye una nueva lista a~nadiendo el primer elemento de la lista a la
lista existente (que en la primera iteración del bucle es nil). Puesto que el segundo
elemento está asignado en frente del segundo elemento, la lista es inversa.
En la expresión que usa el bucle while, la expresión (setq list (cdr list))
ordena la lista, ası́ el bucle while finalmente para. Además, se proporciona la ex-
presión cons con un nuevo primer elemento creando una nueva lista y se ordena en
cada repetición del bucle.
La expresión dolist hace lo mismo que la expresión while, excepto que la macro
dolist hace algo del trabajo que se tiene que hacer cuando se escribe una expresión
while.
122 Capı́tulo 11: Bucles y recursión
Al igual que el bucle while, tenemos el bucle dolist. Lo que es diferente es que
automáticamente ordena la lista cada vez que se repite — eso es ‘recorrer los cdrs
de la lista’ en sı́ — y eso automáticamente asocia el car de cada versión ordenada
de la lista al primero de sus argumentos.
En el ejemplo, el car de cada versión ordenada de la lista se refiere a usar el
sı́mbolo ‘element’, la lista en sı́ se llama ‘list’, y el valor devuelto se llama ‘value’.
El resto de la expresión dolist es el cuerpo.
La expresión dolist asocia el car de cada versión ordenada de la lista al
element y entonces evalúa el cuerpo de la expresión y repite el bucle. El resul-
tado es devuelto en value.
La macro dotimes
La macro dotimes es similar a dolist, excepto que el bucle se repite un número
especı́fico de veces.
El primer argumento dotimes se asigna a los números 0, 1, 2 y ası́ vuelve al
bucle, y el valor del tercer argumento se devuelve. Se necesita proveer el valor del
segundo argumento, que es cuántas veces la macro hace el bucle.
Por ejemplo, lo siguiente asocia los números de 0 en adelante, pero no incluyendo,
el número 3 al primer argumento, número, y entonces construye una lista de los tres
números. (El primer número es 0, el segundo número es 1, y el tercer número es
2; esto crea un total de tres números en todo, empezando con cero como el primer
número.)
(let (value) ; de otro modo un valor es una variable vacı́a
(dotimes (number 3 value)
(setq value (cons number value))))
⇒ (2 1 0)
dotimes devuelve value, ası́ el camino para usar dotimes es para operar en alguna
expresión el número de veces number y entonces devolver el resultado, como una
lista o un átomo.
Aquı́ hay un ejemplo de una defun que usa dotimes para a~ nadir el número de
asteriscos en un triángulo.
(defun triangle-using-dotimes (number-of-rows)
"Usando dotimes, a~
nade el número de asteriscos en un triángulo."
(let ((total 0)) ; de otro modo un total es una variable vacı́a
(dotimes (number number-of-rows total)
(setq total (+ total (1+ number))))))
(triangle-using-dotimes 4)
11.3 Recursión
Una función recursiva contiene código que hace que el intérprete Lisp llame a un
programa que ejecute el código en sı́, pero con argumentos ligeramente diferentes.
El código ejecuta exactamente lo mismo porque eso tiene el mismo nombre. Sin em-
bargo, incluso aunque el programa tenga el mismo nombre, no es la misma entidad.
Eso es diferente. En la jerga, se dice es una ‘instancia’ diferente.
Sección 11.3: Recursión 123
miran ası́ misteriosamente de manera tan simple como incompresible. Como montar
en bicicleta, leer una función recursiva es duro al principio, pero después es simple.
Hay varios patrones recursivos diferentes. Un patrón muy simple se parece a:
(defun name-of-recursive-function (argument-list)
"documentation..."
(if do-again-test
body...
(name-of-recursive-function
next-step-expression)))
Cada vez que una función recursiva es evaluada, una nueva instancia se crea y
se dice qué hacer. Los argumentos le dicen a la instancia qué hacer.
Un argumento se empareja al valor de la next-step-expresion. Cada instancia se
ejecuta con un valor diferente de la next-step-expression.
El valor en la next-step-expression es usado en la do-again-test.
El valor devuelto por la next-step-expression es pasada a las nuevas instancias
de la función, que lo evalúa (o alguna transformación de eso) para determinar si
continuar o parar. El next-step-expression está dise~ nado ası́ que el do-again-test
devuelve falso cuando la función no se repetirı́a mucho.
El do-again-test es algunas veces llamado la condición de parar, puesto que sirve
para parar las repeticiones cuando se devuelve falso.
(print-elements-recursively animales)
La función print-elements-recursively primero chequea si hay cualquier con-
tenido en la lista; si lo hay, la función imprime el primer elemento de la lista, el
car de la lista. Entonces la función se ‘invoca en sı́’, pero da a sı́ mismo como su
Sección 11.3: Recursión 125
jirafa
leon
tigre
nil
(triangle-recursively 7)
126 Capı́tulo 11: Bucles y recursión
Un argumento de 3 o 4
Supón que triangle-recursively es llamado con un argumento de 3.
Sección 11.3: Recursión 127
(print-elements-recursively animales)
130 Capı́tulo 11: Bucles y recursión
(+ 7 (triangle-recursively 6))
La primera instancia de triangle-recursively — se puede querer pensar como un
peque~no robot — no puede completar su trabajo. Eso debe manejar el cálculo para
(triangle-recursively 6) a una segunda instancia del programa, a un segundo
robot. Este segundo individuo es completamente diferente desde el primero; eso es,
en la jerga, una ‘diferente instanciación’. O, poner otro camino, eso es un diferente
robot. Eso es el mismo modelo como el primero; eso calcula números de triángulo
recursivamente; pero eso tiene un número de serie diferente.
¿Y qué hace (triangle-recursively 6) devuelve? Eso devuelve el número 6
a~
nadido al valor devuelto para evaluar triangle-recursively con un argumento
de 5. Usando la metáfora del robot, eso cuestiona todavı́a otro robot para ayudarle.
Ahora el total es:
(+ 7 6 (triangle-recursively 5))
¿Y qué ocurre después?
(+ 7 6 5 (triangle-recursively 4))
Cada vez que triangle-recursively es llamado, excepto por la última vez, eso
crea otra instancia del programa — otro robot — y pregunta para crear un cálculo.
Finalmente, la adición completa es de la siguiente manera:
(+ 7 6 5 4 3 2 1)
Este dise~
no para la función difiere el cálculo del primer paso hasta el segundo
puede ser hecho, y difiere esto hasta que el tercero puede ser hecho, y ası́. Cada
defermento significa el ordenador debe recordar que está siendo esperado dentro.
Esto no es un problema cuando hay solo unos pocos pasos, como en este ejemplo.
Pero eso puede ser un problema cuando hay más pasos.
2
La frase cola recursiva es usado para describir tal proceso, uno que usa ‘espacio
constante’.
Sección 11.3: Recursión 133
(triangle-recursive-helper 0 1 1)
De nuevo, (> counter number) será falso, ası́ de nuevo, el intérprete Lisp eva-
luará triangle-recursive-helper, creando una nueva instancia con nuevos argu-
mentos.
3
La jerga es medianamente confusa: triangle-recursive-helper usa un proceso que
es iterativo en un procedimiento que es recursivo. El proceso se llama iterativo porque
el ordenador necesita solo grabar los tres valores, suma, contador, y número: el pro-
cedimiento es recursivo porque la función ‘llama a sı́ mismo’. Por otro lado, ambos el
proceso y el procedimiento usado por triangle-recursively son llamados recursivos.
La palabra ‘recursivo’ tiene diferentes significados en los dos contextos.
134 Capı́tulo 11: Bucles y recursión
que es:
(triangle-recursive-helper 1 2 1)
En este caso, el test (> counter number) ¡será cierto! Ası́ la instancia devolverá
el valor de la suma, que será 1, como se espera.
Ahora, permite pasar triangle-initialization un argumento de 2, para en-
contrar cuántos asterisco hay en un triángulo con dos filas.
Esta función llama (triangle-recursive-helper 0 0 2).
En fases, las instancias llamadas serán:
suma contador número
(triangle-recursive-helper 0 1 2)
(triangle-recursive-helper 1 2 2)
(triangle-recursive-helper 3 3 2)
Cuando la última instancia se llama, el (> counter number) se chequea si será
cierto, ası́ la instancia devolverá el valor de sum, que será 3.
Este tipo de patrón ayuda cuando estás escribiendo funciones que puede usar
recursos en un ordenador.
Las búsquedas expresiones regulares son usadas extensivamente en GNU Emacs. Las
dos funciones forward-sentence y forward-paragraph, ilustran estas búsquedas
bien. Usan expresiones regulares para encontrar donde mover el punto. La frase
‘expresión regular’ es con frecuencia escrita como ‘regexp’.
Las búsquedas de expresiones regulares son descritas en Sección “Búsqueda de
Expresión Regular” in El Manual de GNU Emacs, tan bien como en Sección “Ex-
presiones Regulares” in El Manual de Referencia de GNU Emacs Lisp. Escribiendo
este capı́tulo, estoy presuponiendo que tiene al menos una familiaridad con esto. El
mayor punto para recordar es que las expresiones regulares te permiten buscar pa-
trones tan bien como para cadenas literales de caracteres. Por ejemplo, el código en
forward-sentence busca para el patrón de posibles caracteres que podrı́an marcar
el fin de una frase, y mueve el punto al otro lado.
Antes de mirar en el código la función forward-sentence, es valorable consi-
derar que el patrón que marca el fin de una frase debe estar. El patrón se discute
en la siguiente sección; siguiendo que es una descripción de la expresión regular
de búsqueda, re-search-forward. La función forward-sentence es descrito en
la sección siguiente. Finalmente, la función forward-paragraph es descrito en la
última sección de este capı́tulo. forward-paragraph es una función compleja que
introduce varias funcionalidades.
Aquı́, ‘$’ indica el fin de la lı́nea, y yo he apuntado donde el tab y dos espacios están
insertados en la expresión. Ambos están insertados poniendo los caracteres actuales
dentro de la expresión.
Dos barras invertidas, ‘\\’, se requiere antes de los paréntesis y barras verticales:
la primera barra invertida cita la siguiente barra invertida en Emacs; y el segundo
indica que el siguiente caracter, el paréntesis o la barra vertical, es especial.
También, una frase puede ser seguida por uno o más retornos de carro, como
este:
[
]*
Como en los tabuladores y espacios, un retorno de carro se inserta dentro de una
expresión regular insertándolo literalmente. El asterisco indica que el RET se repite
cero o más veces.
Pero una frase no consiste solo en un periodo, una marca de pregunta o una
marca de exclamación seguida por espacios apropiados: una marca de cerrar comillas
o cerrar un paréntesis de algún tipo puede preceder el espacio. En realidad más de
una marca o paréntesis pueden preceder el espacio. Estas requieren una expresión
que se parezca a:
[]\"’)}]*
En esta expresión, el primer ‘]’ es el primer caracter en la expresión; el segundo
caracter es ‘"’, que está precedido por un ‘\’ para contar Emacs el ‘"’ no es especial.
Los últimos tres caracteres son ‘’’, ‘)’, y ‘}’.
Todo esto sugiere que el patrón de la expresión regular para asociar el fin de
una frase serı́a; y, profundamente, si se evalúa sentence-end y encuentra que se
devuelve el valor siguiente:
sentence-end
⇒ "[.?!][]\"’)}]*\\($\\| \\| \\)[
]*"
(Bien, no en GNU Emacs 22; porque es un esfuerzo crear el proceso simple y ma-
nejar más sı́mbolos y lenguajes. Cuando el valor de sentence-end es nil, entonces
usa el valor definido por la función sentence-end es nil, entonces usa el valor de-
finido por la función sentence-end. (Aquı́ se usa la diferencia entre un valor y una
función en Emacs Lisp.) La función devuelve un valor construido desde las varia-
bles sentence-end-base, sentence-end-double-space, sentence-end-without-
period, y sentence-end-without-space. La variable crı́tica es sentence-end-
base; su valor global es similar a uno descrito debajo pero también contiene
marcas de cita adicionales. Estas tienen diferentes grados de curvas. La variable
sentence-end-without-period, cuando es verdad, dice a Emacs que una frase
puede finalizar sin un periodo tal como texto en Thai.)
es hacia atrás, deja el punto antes del primer caracter en el objetivo. Se puede
contar re-search-forward para devolver t a cierto. (Moviendo el punto es por ello
un ‘efecto lateral’.)
Como search-forward, la función re-search-forward toma cuatro argumen-
tos:
12.3 forward-sentence
El comando mueve el cursor hacia adelante una frase es una ilustración honesta
de cómo usar búsquedas de expresiones regulares en Emacs Lisp. En realidad, la
función parece más larga y más complicada de lo que es; esto es porque la función
está dise~
nada para ir hacia atrás tan bien como hacia adelante; y, opcionalmente, a
través de una frase. La función está normalmente asociada al comando M-e.
138 Capı́tulo 12: Búsquedas de expresiones regulares
Pero primero, permı́tenos examinar cómo par-end se asocia a la variable del fin
del párrafo. Lo que ocurre es que el let asigna el valor de par-end al valor devuelto
cuando el intérprete evalúa la expresión.
(save-excursion (end-of-paragraph-text) (point))
En esta expresión, (end-of-paragraph-text) mueve el punto al fin del párrafo,
(point) devuelve el valor del punto, y entonces save-excursion restaura el punto
a su posición original. De este modo, el let asocia par-end al valor devuelto por
la expresión save-excursion, que es la posición del fin del párrafo. (La función
end-of-paragraph-text usa forward-paragraph, que se discutirá pronto.)
Emacs evalúa el cuerpo del let, que es una expresión if que se parece a esto:
(if (re-search-forward sentence-end par-end t) ; if-part
(skip-chars-backward " \t\n") ; then-part
(goto-char par-end))) ; else-part
El test if si su primer argumento es cierto y si ası́, evalúa su parte then; de
otro modo, el intérprete Emacs Lisp evalúa la parte else. El true-or-false-test de la
expresión if es la búsqueda de la expresión regular.
Puede estar mal tener que mirar como el ‘trabajo real’ de la función
forward-sentence es vista aquı́, pero esto es un camino común de este tipo de
operación traida en Lisp.
La expresión let*
La siguiente lı́nea de la función forward-paragraph empieza una expresión let*.
Esto es tan diferente como let. El sı́mbolo es let* no let.
La forma especial let* es como let excepto que Emacs asigna cada variable en
secuencia, una después de otra, y las variables en la última parte de la varlist hacen
uso de los valores para los que Emacs asignó variable al principio la varlist.
(Sección 4.4.3 “save-excursion en append-to-buffer”, página 53.)
En la expresión let* en esta función, Emacs asigna un total de siete varia-
bles: opoint, fill-prefix-regexp, parstart, parsep, sp-parstart, start, y
found-start.
La variable parsep aparece dos veces, primero, para borrar instancias de ‘^’, y
segundo, para manejar prefijos rellenos.
La variable opoint es solo el valor de point. Como se puede adivinar, eso se
usa en una expresión constrain-to-field, solo como en forward-sentence.
La variable fill-prefix-regexp se asigna al valor devuelto para evaluar la
siguiente lista:
(and fill-prefix
(not (equal fill-prefix ""))
(not paragraph-ignore-fill-prefix)
(regexp-quote fill-prefix))
Esta es una expresión cuyo primer elemento es la forma especial and.
Como se aprendió antes la (véase “La función kill-new”, página 94), la forma
especial and evalúa cada uno de sus argumentos hasta uno de los argumentos y
devuelve un valor de nil en el que el caso de la expresión and devuelve nil; sin
embargo, si ninguno de los argumentos devuelve un valor de nil, el valor resultante
de evaluar el último argumento es devuelto. (Puesto que tal valor no es nil, eso
es considerado verdad en Lisp.) En otras palabras, una expresión and devuelve un
valor de verdad solo si todos sus argumentos son verdad.
En este caso, la variable fill-prefix-regexp está asociado a un valor no nil
solo si el las siguientes cuatro expresiones producen un valor true (por ej., un no
nil) cuando son evaluados; de otro modo, fill-prefix-regexp está asociado a
nil.
fill-prefix
Cuando esta variable se evalúa, el valor del prefijo de relleno fill pre-
fix, si cualquiera, está devuelto. Si no hay prefijo relleno, la variable
devuelve nil.
(not paragraph-ignore-fill-prefix)
Esta expresión devuelve nil si la variable paragraph-ignore-fill-
prefix ha sido cambiado siendo asignado un valor de verdad tal como
t.
(regexp-quote fill-prefix)
Este es el último argumento para la forma especial and. Si todos los
argumentos de and son verdaderos, el valor resultante de evaluar esta
expresión será devuelto por la expresión and y asociado a la variable
fill-prefix-regexp,
El resultado de evaluar esta expresión and con éxito es que fill-prefix-regexp se
asociará al valor de fill-prefix como fué modificado por la función regexp-quote.
Lo que regexp-quote hace es leer una cadena y devolver la expresión regular que
asociará exactamente la cadena y nada más. Esto significa que fill-prefix-regexp
será asignada a un valor que asociará el prefijo si el prefijo existe. De otro modo, la
variable será asignada a nil.
Las dos variables locales siguientes en la expresión let* están dise~ nadas para
eliminar instancias de ‘^’ desde parstart y parsep, las variables locales indican
que el párrafo empieza como separador de párrafo. La siguiente expresión asigna
parsep de nuevo. Esto es manejar prefijos rellenos.
Esta es la configuración que requiere la llamada de la definición let* en vez de
let. El true-or-false-test para el if depende de si la variable fill-prefix-regexp
evalúa a nil o algún otro valor.
Si fill-prefix-regexp no tiene un valor, Emacs evalúa la parte else de la
expresión if y asocia parsep a su valor local. (parsep es una expresión regular que
asocia lo que los párrafos separan.)
Pero si fill-prefix-regexp tiene un valor, Emacs evalúa la parte then de la
expresión if y asocia parsep a una expresión regular que incluye el fill-prefix-
regexp como parte del patrón.
Especı́ficamente, parsep está asignado al valor original del párrafo que separa
la expresión regular concatenada con una expresión alternativa que consiste del
fill-prefix-regexp seguido por espacios en blanco opcionales para el fin de la
lı́nea. El espacio en blanco está definido por "[ \t]*$".) El ‘\\|’ define esta porción
del regexp como una alternativa a parsep.
De acuerdo a un comentario en el código, la siguiente variable local,
sp-parstart, se usa para buscar, y entonces los dos finales, start y found-start,
se asignan a nil.
Ahora tenemos dentro el cuerpo del let*. La primera parte del cuerpo del let*
trata con el caso cuando la función es dada a un argumento negativo y consiguien-
temente moviéndose hacia atrás. Nosotros saldremos de esta sección yendo hacia
atrás.
común de la función el valor del argumento es 1, ası́ el cuerpo del bucle while se
evalúa exactamente una vez, y el cursor se mueve hacia adelante un párrafo.
Esta parte maneja tres situaciones: cuando el punto está entre párrafos, cuando
hay un prefijo de relleno y cuando no hay prefijo de relleno fill prefix.
El bucle while se parece a esto:
;; yendo hacia adelante y no al fin del búffer
(while (and (> arg 0) (not (eobp)))
;; entre párrafos
;; Mueve hacia adelante a través de lı́neas de
;; separación...
(while (and (not (eobp))
(progn (move-to-left-margin) (not (eobp)))
(looking-at parsep))
(forward-line 1))
;; Esto decremento el bucle
(unless (eobp) (setq arg (1- arg)))
;; ... y una lı́nea más
(forward-line 1)
(if fill-prefix-regexp
;; Hay un prefijo lleno; que sobreescribe parstart;
;; vamos adelante lı́nea por lı́nea
(while (and (not (eobp))
(progn (move-to-left-margin) (not (eobp)))
(not (looking-at parsep))
(looking-at fill-prefix-regexp))
(forward-line 1))
;; No hay prefijo;
;; vamos hacia adelante caracter por caracter
(while (and (re-search-forward sp-parstart nil 1)
(progn (setq start (match-beginning 0))
(goto-char start)
(not (eobp)))
(progn (move-to-left-margin)
(not (looking-at parsep)))
(or (not (looking-at parstart))
(and use-hard-newlines
(not (get-text-property (1- start) ’hard)))))
(forward-char 1))
El bucle while nos tiene buscando hacia adelante para sp-parstart, que es la com-
binación de posibles espacios en blanco con un valor local del comienzo de un párrafo
o de un párrafo separador. (Las últimas dos son con una expresión empezando con
(?:) ası́ que no están referenciadas por la función match-beginning.)
Las dos expresiones,
(setq start (match-beginning 0))
(goto-char start)
significa ir al comienzo del siguiente texto localizado por la expresión regular.
La expresión (match-beginning 0) es nueva. Eso devuelve un número especifi-
cando la posición del comienzo del texto fuese asociado a la última búsqueda.
La función match-beginning es usado aquı́ porque una caracterı́stica de una
búsqueda hacia adelante: una búsqueda hacia adelante, sin dignidad si eso es una
búsqueda plana o una expresión regular, mueve el punto al fin del texto que es
encontrado. En este caso, una búsqueda exitosa mueve el punto al fin del patrón
para sp-parstart.
Sin embargo, se quiere poner el punto al fin del actual párrafo, no en algún lugar
más. En vez de eso, desde que la búsqueda posiblemente incluye el separador del
párrafo, el punto puede finalizar al principio de lo siguiente a menos que se use una
expresión que incluya match-beginning.
Cuando un argumento de 0, match-beginning devuelve la posición que es el
comienzo del texto asociado por la búsqueda más reciente. En este caso, la búsqueda
más reciente parece sp-parstart. La expresión (match-beginning 0) devuelve la
posición del comienzo de este patrón, en vez de la posición final de este patrón.
(Incidentalmente, cuando se pasa un número positivo como un argumento, la
función match-beginning devuelve la localización de punto en el que la expresión
con paréntesis en la última búsqueda a menos que la expresión con paréntesis em-
piece con \(?:. No sé porque \(?: aparece aquı́ desde que el argumento es 0.)
La última expresión cuando no hay prefijos es
(if (< (point) (point-max))
(goto-char start))))
Esto dice que si no hay prefijo lleno y no estamos al punto final que se moverı́a
al principio de lo que fué encontrado por la búsqueda de la expresión regular para
sp-parstart.
La definición completa para la función forward-paragraph no solo incluye
código para avanzar, también código para retroceder.
Si está leyendo esto dentro de GNU Emacs y quieres ver la función completa,
se puede escribir C-h f (describe-function) y el nombre de la función. Esto da
la documentación de función y el nombre de la librerı́a conteniendo las fuentes de
la función. Posiciona el punto a través del nombre de la librerı́a y presionar la tecla
RET; será tomado directamente a las fuentes. (¡Asegúrate de instalar las fuentes!
¡Sin eso, estarás como una persona que intenta conducir un coche con los ojos
cerrados!)
Sección 12.5: Crea tu propio fichero TAGS 147
Si piensa que un fichero TAGS apropiado que ya existe para lo que quieres, pero
no conoces donde está, se puede usar el programa locate para intentar encontrarlo.
Escribe M-x locate RET TAGS RET y Emacs listará para ti las rutas nombres
completas de todos tus ficheros TAGS. En mi sistema, este comando lista 34 fichero
TAGS. Por otro lado, un sistema ‘vanilla plano’ que recientemente no contenı́a fichero
TAGS.
Si la tabla de etiquetas que se quiere ha sido creada, se puede usar el comando
M-x visit-tags-table para especificarlo. De otro modo, se necesitará la tabla de
etiquetas por tı́ mismo y entonces usar M-x visit-tags-table.
12.6 Revisar
Aquı́ hay un breve resumen de algunas funciones introducidas recientemente.
while Repetidamente evalúa el cuerpo de la expresión tan larga como el
primer elemento del cuerpo chequea cierto. Entonces devuelve nil.
(La expresión es evaluado solo por sus efectos laterales.)
Por ejemplo:
(let ((foo 2))
(while (> foo 0)
(insert (format "foo is %d.\n" foo))
(setq foo (1- foo))))
⇒ foo is 2.
foo is 1.
nil
(La función insert inserta sus argumentos en el punto; la función
format devuelve una cadena formateada desde sus argumentos el ca-
mino message formatea sus argumentos; \n produce una nueva lı́nea.)
re-search-forward
Busca un patrón, y si el patrón se encuentra, mueve el punto al resto
solo después de eso.
Toma cuatro argumentos, como search-forward:
Sección 12.7: Ejercicios con re-search-forward 149
match-beginning
Devuelve la posición del principio del texto encontrado por la última
búsqueda de la expresión regular.
looking-at
Devuelve t para verdadero si el texto después del punto se asocia al
argumento, que deberı́a ser una expresión.
eobp Devuelve t para cierto si el punto está en el fin de la parte accesible
de un búffer. El fin de la parte accesible es el fin del búffer no está
encogido; eso es el fin de la parte encogida si el búffer está encogido.
(Note que las barras invertidas que preceden el ‘w’ y ‘W’. Una barra invertida tiene
significado especial al intérprete Emacs Lisp. Eso indica que el caracter siguiente
es interpretado de manera diferente que la normal. Por ejemplo, los dos caracteres,
‘\n’, son una ‘nueva lı́nea’, en vez de una barra invertida seguida por ‘\n’. Dos
barras invertidas en una fila para una ‘barra invertida no especial’, ası́ Emacs Lisp
interpreta el fin de mirar una barra invertida simple seguida por una letra. Ası́
descubre la letra que es especial.)
Se necesita un contador para contar cuántas palabras hay; esta variables debe
primero ser asignado a 0 y entonces incrementados cada vez que Emacs va alrededor
del bucle while. La expresión de incremento es simple:
(setq count (1+ count))
hace nada más. En particular, no mueve el punto, que hace como un efecto lateral
si se encuentra la búsqueda objetiva. Después la expresión re-search-forward
devuelve nil, la siguiente expresión en el bucle while está evaluado. Esta expresión
incrementa el contador. Entonces el bucle repite. El test true-or-false-test chequea
cierto porque el valor del punto es todavı́a menor que el valor final, desde que la
expresión re-search-forward no moverı́a el punto. . . . y el ciclo repite . . .
Por ambas la primera condición y la segunda condición deben ser ciertas juntas,
las dos expresiones, la región chequea y la expresión de búsqueda, puede estar unido
con una forma especial and y embebido en el bucle while como el true-or-false-test,
como esto:
(and (< (point) end) (re-search-forward "\\w+\\W*" end t))
(Para información acerca de and, ver “La función kill-new”, página 94.)
que~no pensamiento sugiere que esto puede ser hecho haciendo uso de una expresión
let al número de palabras en la región, como se devuelve por la llamada recursiva; y
entonces la expresión cond, que usa la asociación, puede mostrar el valor al usuario.
Con frecuencia, uno piensa que se puede asociar una expresión let como algo
secundario al trabajo ‘primario’ de una función. Pero en este caso, se podrı́a con-
siderar el trabajo ‘primario’ de la función, contando palabras, esto es hecho con la
expresión let.
Usando let, la definición de función se parece a:
(defun count-words-example (beginning end)
"Imprime el número de palabras en la región."
(interactive "r")
adelante por una palabra, y desde que una llamada recursiva es hecha para cada
palabra, el mecanismo de contaje debe ser una expresión que a~
nade uno al valor
devuelto por una llamada para recursive-count-words
Considera varias casos:
• Si hay dos palabras en la región, la función devolverá un valor resultante de
a~
nadir uno al valor devuelto al contar la primera palabra, más el número
devuelto al contar las palabras que permanecen en la región, que en este caso
es una.
• Si hay una palabra en la región, la función devolverı́a un valor resultante de
a~
nadir uno al valor devuelto cuando eso cuenta esta palabra más el número
devuelto cuando eso cuenta las palabras que permanecen en la región, que en
este caso es cero.
• Si no hay palabras en la región, la función devolverı́a cero.
Desde el esquema se puede ver que la parte else del if devuelve cero para el
caso en el que no hay palabras. Esto significa que la parte then del if debe devolver
un valor resultante de a~
nadir uno al valor devuelto desde el contaje de las palabras
que permanecen.
La expresión se parece a esto, donde 1+ es una función que a~ nade uno a su
argumento.
(1+ (recursive-count-words region-end))
La función completa recursive-count-words entonces se parecerá e esto:
(defun recursive-count-words (region-end)
"documentation..."
;;; 1. do-again-test
(if (and (< (point) region-end)
(re-search-forward "\\w+\\W*" region-end t))
;;; 3. else-part
0))
Permı́teme examinar cómo esto funciona:
Si no hay palabras en la región, la parte else de la expresión if es evaluada y,
por tanto, la función devuelve cero.
Si hay una palabra en la región, el valor del punto es menor que el valor de
region-end y la búsqueda tiene éxito. En este caso, el true-or-false-test de la ex-
presión if chequea cierto, y la then-part de la expresión if es evaluada. La expresión
de contaje se evalúa. Esta expresión devuelve un valor (que será el valor devuelto
por la función completa) que es la suma de uno a~ nadida al valor devuelto por una
llamada recursiva.
Mientras tanto, la next-step-expression ha causado el punto para saltar a través
de la primera (y en este caso única) palabra en la región. Esto significa que cuando
(recursive-count-words region-end) está evaluada una segunda vez, como un
resultado de la llamada recursiva, el valor del punto será igual o mayor que el valor
160 Capı́tulo 13: Contando: repetición y regexps
;;; 1. do-again-test
(if (and (< (point) region-end)
(re-search-forward "\\w+\\W*" region-end t))
;;; 3. else-part
0))
El envoltorio:
;;; Versión Recursiva
(defun count-words-example (beginning end)
"Imprime el número de palabras en la región.
También, recuérdanos que un bucle while devuelve nil (el resultado de evaluar
el true-or-false-test), no el resultado de cualquier evaluación con su cuerpo. (Las
evaluaciones con el cuerpo del bucle son hechas para sus efectos laterales.) Sin
embargo, la expresión que asigna la lista de tama~ nos es parte del cuerpo — y que
es el valor que queremos devuelto por la función como un todo. Para hacer esto
cerramos el bucle while con una expresión let, y pone en orden que el último
elemento de la expresión let contiene el valor de lista de tama~ nos. (Véase “El
Ejemplo del Bucle con un Contador de Incremento”, página 115.)
Estas consideraciones lideran directamente a la función en sı́:
;;; Usar bucle while.
(defun lengths-list-many-files (list-of-files)
"Devuelve la lista de tama~
nos de funciones en LIST-OF-FILES."
(let (lengths-list)
;;; true-or-false-test
(while list-of-files
(setq lengths-list
(append
lengths-list
que construye una nueva lista en el que el primer argumento para cons llega a ser
el primer elemento de la nueva lista:
((1 2 3 4) 5 6 7 8)
(lengths-list-file "./lisp/macros.el")
⇒ (283 263 480 90)
(lengths-list-file "./lisp/mail/mailalias.el")
⇒ (38 32 29 95 178 180 321 218 324)
(lengths-list-file "./lisp/makesum.el")
⇒ (85 181)
Sección 14.9: Preparar los datos para mostrarlos en un grafo 173
(recursive-lengths-list-many-files
’("./lisp/macros.el"
"./lisp/mail/mailalias.el"
"./lisp/makesum.el"))
⇒ (283 263 480 90 38 32 29 95 178 180 321 218 324 85 181)
La función recursive-lengths-list-many-files produce la salida que quere-
mos.
El siguiente paso es preparar el dato en la lista para mostrarlo en un grafo.
La función < se usa cuando se ordena una lista numérica. Por ejemplo,
(sort ’(4 8 21 17 33 7 21 7) ’<)
produce esto:
(4 7 7 8 17 21 21 33)
(Note que en este ejemplo, ambos argumentos se citan ası́ que los sı́mbolos no se
evalúan antes de ser pasados por sort como argumentos.)
Ordenando la lista devuelta por la función recursive-lengths-list-many-
files es honesta; eso usa la funciń <:
(sort
(recursive-lengths-list-many-files
’("./lisp/macros.el"
"./lisp/mailalias.el"
"./lisp/makesum.el"))
’<)
que produce:
(29 32 38 85 90 95 178 180 181 218 263 283 321 324 480)
(Note que en este ejemplo, el primer argumento para sort no está citado, desde que
la expresión debe ser evaluado ası́ como producir la lista que es pasada para sort.)
Se permite escribir una definición de función para hacer estas tareas. Se usará un
bucle while para mover de un nombre de fichero a otro con un directorio chequeando
lo que necesita ser hecho; y se usa una llamada recursiva para repetir las acciones
en cada subdirectorio. El patrón recursivo es ‘acumular’ (véase “Patrón recursivo:
accumulate”, página 130) usando append para combinar.
Aquı́ está la función:
(defun files-in-below-directory (directory)
"Lista los ficheros .el en DIRECTORIO y en sus subdirectorios."
;; Aunque la función será usada no interactivamente,
;; será fácil chequear si lo hacemos interactivo.
;; El directorio tendrá un nombre tal como
;; "/usr/local/share/emacs/22.1.1/lisp/"
(interactive "DNombre del Directorio: ")
(let (el-files-list
(current-directory-list
(directory-files-and-attributes directory t)))
;; mientras estamos en el directorio actual
(while current-directory-list
(cond
;; chequee para ver si el nombre del fichero finaliza en ‘.el’
;; y si es ası́, a~nade su nombre a una lista.
((equal ".el" (substring (car (car current-directory-list)) -3))
(setq el-files-list
(cons (car (car current-directory-list)) el-files-list)))
;; chequee si el nombre del fichero es un directorio
((eq t (car (cdr (car current-directory-list))))
;; decide si salir o hacer recursión
(if
(equal "."
(substring (car (car current-directory-list)) -1))
;; entonces no hagas nada puesto que el nombre del fichero es
;; el actual directorio o padre, "." o ".."
()
Sección 14.9: Preparar los datos para mostrarlos en un grafo 177
finalizará el primer número más largo. Pero lo que se quiere es imprimir nuestro grafo
con peque~ nos valores primero y el más grande después. La solución es invertir el
orden del defuns-per-range-list. Nosotros podemos hacer esto usando la función
nreverse, que invierte el orden de una lista.
Por ejemplo,
produce:
(4 3 2 1)
;; Bucle Exterior.
(while top-of-ranges
;; Bucle Interno.
(while (and
;; Necesita el número para el test numérico.
(car sorted-lengths)
(< (car sorted-lengths) top-of-range))
(setq defuns-per-range-list
(cons number-within-range defuns-per-range-list))
(setq number-within-range 0) ; Resetear el contaje a cero.
(setq sorted-lengths
’(85 86 110 116 122 129 154 176 179 200 265 300 300))
15 Leyendo un grafo
correcto de lı́neas bajo la base del gráfico para imprimirse correctamente. Esto
podrı́a ser difı́cil.
Alternativamente, si podemos figurarnos algún camino para pasar
insert-rectangle del mismo tama~ no cada vez, entonces podemos posicionar el
punto en la misma lı́nea cada vez, pero se mueve a través de una columna a la
derecha por cada nueva columna. Si hacemos esto, sin embargo, alguna de las
entradas en la lista pasaba a insert-rectangle y deben ser espacios en blanco en
vez de asteriscos. Por ejemplo, si la altura máxima del grafo es 5, pero la altura
de la columna es 3, entonces insert-rectangle requiere un argumento que se
parezca a esto:
(" " " " "*" "*" "*")
Esta última propuesta no es tan difı́cil, de hecho se puede determinar la altura
de la columna. Hay dos caminos para nosotros especificar la altura de la columna:
se puede arbitrariamente situar lo que será, lo que funcionarı́a bien para gráficas
de esta altura; o podemos buscar a través de la lista de números y usar la altura
máxima de la lista como la altura máxima del grafo. Si la última operación fuera
difı́cil, entonces el procedimiento formal serı́a fácil, pero hay una función construida
en Emacs para determinar el máximo de sus argumentos. Se puede usar esta función.
La función se llamaba max y eso devuelve el mayor de sus argumentos, que deben
ser números. De este modo, por ejemplo,
(max 3 4 6 5 7 3)
devuelve 7. (Una función correspondiente llamada min devuelve lo más peque~no de
todos sus argumentos.)
Sin embargo, no podemos simplemente llama a max en el numbers-list; la
función max espera números como sus argumentos, no una lista de números. De este
modo, la siguiente expresión,
(max ’(3 4 6 5 7 3))
produce el siguiente mensaje error;
Mal tipo de argumento: number-or-marker-p, (3 4 6 5 7 3)
Se necesita una función que pasa una lista de argumentos a una función. Esta
función es apply. Esta función ‘aplica’ su primer argumento (una función) para los
argumentos que permanecen, el último puede ser una lista.
Por ejemplo,
(apply ’max 3 4 7 3 ’(4 8 5))
devuelve 8
(Incidentalmente, yo no sabrı́a cómo aprender acerca de esta función sin un
libro tal como este. Eso es posible descubrir otras funciones, como search-forward
o insert-rectangle, adivinando una parte de sus nombres y entonces usando
apropos. Incluso aunque su base metafórica es clara — ‘apply’ su primer argumento
al resto — dudo que un novicio vendrı́a con esta palabra particular usando apropos
u otra ayuda. De acuerdo, podrı́a ser incorrecto; después de todo, la función fué
primero llamada por alguien quien lo habı́a inventado.
El segundo y subsiguientes argumentos para apply son opcionales, ası́ se puede
usar apply para llamar a una función y pasan los elementos de una lista, como este,
que también devuelve 8:
185
;; Llenar asteriscos.
(while (> actual-height 0)
(setq insert-list (cons "*" insert-list))
(setq actual-height (1- actual-height)))
para el asterisco. Se podrı́a incluso querer hacer un graph-column que es más que
un ancho de columna. El programa deberı́a ser más flexible. El camino para hacer
esto es reemplazar el espacio en blanco y el asterisco con dos variables que se puede
llamar graph-blank y graph-symbol y define estas variables separadamente.
También la documentación no está escrita. Estas consideraciones nos llevan tam-
bién a la segunda versión de la función:
(defvar graph-symbol "*"
"Cadena usada como sı́mbolo en grafo, normalmente un asterisco.")
;; Rellenar en graph-symbols.
(while (> actual-height 0)
(setq insert-list (cons graph-symbol insert-list))
(setq actual-height (1- actual-height)))
;; Rellenar en graph-blanks.
(while (> number-of-top-blanks 0)
(setq insert-list (cons graph-blank insert-list))
(setq number-of-top-blanks
(1- number-of-top-blanks)))
Es fácil ver como escribir tal función, pero puesto que no se necesita eso, no se
hará. Pero el trabajo podrı́a ser hecho, y si eso se hiciera, se harı́a con column-of-
graph. Incluso más importante, no se valora nada más que pocos cambios que
tendrı́an que ser hechos de cualquier otra manera. La mejora, que se desea hacer,
es simple.
Ahora, finalmente, volvemos a nuestra primera función de grafo impresa. Esto
imprime el cuerpo de un grafo, no las etiquetas para los ejes horizontal y vertical,
ası́ se puede llamar este graph-body-print.
(while numbers-list
insert-columns-and-reposition-point
(setq numbers-list (cdr numbers-list)))))
Necesitamos rellenar los slots de la plantilla.
Claramente, se puede usar la expresión (apply ’max numbers-list) para de-
terminar el alto del grafo.
El bucle while iterará a través de numbers-list un elemento a la vez. Como
eso está ordenado por la expresión (setq numbers-list (cdr numbers-list)), el
car de cada instancia de la lista es el valor del argumento para column-of-graph.
En cada ciclo del bucle while, la función insert-rectangle inserta la lista
devuelta por column-of-graph. Desde que la función insert-rectangle, se nece-
sita guardar la localización de punto al tiempo que el rectángulo se inserta, mueve
atrás a esta posición después de que el rectángulo es insertado, y entonces se mueve
horizontalmente al siguiente lugar desde el que insert-rectangle se llama.
Si las columnas se insertan en un carácter amplio, será si los espacios en blanco
y asteriscos se usan, el comando de reposición es simple (forward-char 1); sin
embargo, el ancho de una columna puede ser más grande que uno. Esto significa que
el comando de reposicionamiento serı́a escrito (forward-char symbol-width). El
mejor lugar para asociar la variable symbol-width al valor del width de la columna
grafo está en la varlist de la expresión let.
188 Capı́tulo 15: Leyendo un grafo
(while numbers-list
(setq from-position (point))
(insert-rectangle
(column-of-graph height (car numbers-list)))
(goto-char from-position)
(forward-char symbol-width)
;; Dibuja la columna del grafo por columna.
(sit-for 0)
(setq numbers-list (cdr numbers-list)))
;; Emplaza el punto para etiquetas de ejes X.
(forward-line height)
(insert "\n")
))
La expresión inesperada en esta función es la expresión (sit-for 0) en el bucle
while. Esta expresión hace que el grafo imprima la operación más interesante para
vigilar lo que serı́a de otro modo. La expresión causa que Emacs pare (sit-for 0)
para un momento cero y entonces redibuje la pantalla. Puesto aquı́, eso causa que
Emacs redibuje la pantalla columna por columna. Sin eso, Emacs no redibujarı́a la
pantalla hasta que la función exista.
Se puede chequear graph-body-print con una peque~ na lista de números.
1. Instala graph-symbol, graph-blank, column-of-graph, que están en
Capı́tulo 15 “Leyendo un grafo”, página 182, and graph-body-print.
2. Copia la siguiente expresión:
(graph-body-print ’(1 2 3 4 6 4 3 5 7 6 5 2 3))
3. Cambia al búffer *scratch* y emplaza el cursor donde quiere que el grafo
empiece.
4. Escribe M-: (eval-expression).
5. Pega la expresión graph-body-print dentro del minibúffer con C-y (yank).
6. Presiona RET para evaluar la expresión graph-body-print
Emacs imprimirá un grafo como este:
*
* **
* ****
*** ****
********* *
************
*************
Sección 15.2: La función recursive-graph-body-print 189
(when numbers-list
(setq from-position (point))
(insert-rectangle
(column-of-graph height (car numbers-list)))
(goto-char from-position)
(forward-char symbol-width)
(sit-for 0) ; Dibuja un gráfico columna por columna.
(recursive-graph-body-print-internal
(cdr numbers-list) height symbol-width)))
Después de la siguiente instalación, esta expresión puede ser chequeada; aquı́
hay un ejemplo:
(recursive-graph-body-print ’(3 2 5 6 7 5 3 4 6 4 3 2 1))
Aquı́ está lo que recursive-graph-body-print produce:
*
** *
**** *
**** ***
* *********
************
*************
190 Capı́tulo 15: Leyendo un grafo
15.4 Ejercicio
Escribe una versión de lı́nea de grafo de la funciones de impresión del grafo.
191
16 Tu fichero .emacs
“No te tiene que gustar Emacs para lo que te gusta” — esto que parece una frase
paradójica es el secreto de GNU Emacs. En realidad, Emacs es una herramienta
genérica. La mayorı́a de la gente que usa Emacs, lo personaliza para ajustarlo a sus
necesidades.
GNU Emacs está mayoritariamente escrito en Emacs Lisp; esto significa que
escribiendo expresiones en Emacs Lisp se puede modificar o extender Emacs.
Hay quien aprecia la configuración por defecto de Emacs. Después de todo,
Emacs empieza en modo C cuando se edita un fichero C, empieza en modo Fortran
cuando se edita un fichero Fortran, y empieza en modo Fundamental cuando se
edita un fichero no adornado. Esto tiene sentido, si no sabes quien está yendo a usar
Emacs. ¿Quién sabe lo que una persona espera hacer con un fichero no adornado?
El modo fundamental es el modo correcto por defecto para tal fichero, tal como el
modo C es lo correcto para editar código C. (Suficientes lenguajes de programación
tienen sintaxis que permiten compartir funcionalidades, tal como el modo C es ahora
proporcionado por el modo CC, la ‘Colección C’.)
Pero cuando se conoce quien está yendo a usar Emacs — tu, tu mismo – entonces
eso tiene sentido para personalizar Emacs.
Por ejemplo, yo raramente quiero el modo Fundamental cuando edito un fiche-
ro de otro modo no distinguido; yo quiero el modo Texto. Esto es por lo que yo
personalizo Emacs: ası́ eso se ajusta a mı́.
Se puede personalizar y extender Emacs escribiendo o adaptando un fichero
~/.emacs. Esto es un fichero de inicialización personal; sus contenidos, escritos en
Emacs Lisp, cuentan a Emacs qué hacer.1
Un fichero ~/.emacs contiene código Emacs Lisp. Se puede escribir este código
por uno mismo; o se puede usar la funcionalidad customize para escribir el código
para ti. Se puede combinar tus propias expresiones y expresiones auto-escritas per-
sonalizadas en tu fichero .emacs.
(Yo prefiero por mı́ mismo escribir mis propias expresiones, excepto para estas,
fuentes particularmente, que se encuentran fáciles de manipular usando el comando
customize. Yo combino los dos métodos.)
La mayorı́a de este capı́tulo es acerca de escribir expresiones por uno mismo. Eso
describe un fichero .emacs simple; para más información, mira Sección “El Fichero
de Inicio” in El Manual GNU Emacs, y la Sección “El Fichero de Inicio” in El
Manual de Referencia GNU Emacs Lisp.
1
Tu puedes también a~ nadir .el para ~/.emacs y llama a un fichero ~/.emacs. En el
pasado, fué prohibido escribir los atajos de teclado extra que el nombre ~/.emacs.el
requiere, pero ahora puedes. El nuevo formato es consistente con las conveniciones de
nombre del fichero Emacs Lisp; el viejo formato guarda la escritura.
192 Capı́tulo 16: Tu fichero .emacs
Cada uno de estos argumentos consiste de una palabra seguido de una palabra
por un valor. Cada palabra clave empieza con los dos puntos ‘:’.
Por ejemplo, la variable de opciones personalizable text-mode-hook se parece
a esto:
(defcustom text-mode-hook nil
"El hook normal se ejecuta cuando se introduce en modo texto y
muchos modos relacionados."
:type ’hook
:options ’(turn-on-auto-fill flyspell-mode)
:group ’data)
El nombre de la variable es text-mode-hook; no tiene valor por defecto; y su cadena
de documentación cuenta lo que hace.
La palabra clave :type le cuenta a Emacs el tipo de datos para los que
text-mode-hook serı́a asignado y como muestra el valor en un búffer de Perso-
nalización.
La palabra clave :options especifica una lista sugerida de valores para la va-
riable. Normalmente, :options se asocia a un gancho (hook. La lista es solo una
sugerencia; esa no es exclusiva; una persona quien asigna la variable puede asignarse
a otros valores; la lista mostrada siguiendo la palabra clave :options se pretende
ofrecer elecciones convenientes a un usuario.
Finalmente, la palabra clave :group cuenta el comando de Personalización de
Emacs en el que el grupo de la variable está localizado. Esto cuenta dónde encon-
tralo.
La función defcustom reconoce más de una docena de palabras clave. Para más
información, mire Sección “Escribiendo las Definiciones de Personalización” in El
Manual de Referencia GNU Emacs Lisp.
Considere text-mode-hook como un ejemplo.
Hay dos caminos para personalizar esta variable. Se puede usar el comando de
personalización o escribir las expresiones apropiadas por uno mismo.
Usando el comando de personalización, se puede escribir:
M-x customize
y encuentre que el grupo para editar ficheros de datos se llama ‘datos’. Introduzca
este grupo. El Hook Disparador es el primer miembro. Se puede hacer click en sus
opciones varias, tal como turn-on-auto-fill, para asignar los valores. Después de
hacer click en el botón.
Guárdalo para Futuras Sesiones
Emacs escribirá una expresión en tu fichero .emacs. Se parecerá a esto:
(custom-set-variables
;; custom-set-variables fué a~
nadido por Custom.
;; Si se edita a mano, tu podrı́as liarte,
;; ası́ que ten cuidado.
;; Tu fichero init contendrı́a solo esta instancia.
;; Si hay más de uno, ellos no quieren trabajar.
’(text-mode-hook (quote (turn-on-auto-fill text-mode-hook-identify))))
(La función text-mode-hook-identify cuenta toggle-text-mode-auto-fill que
buffers hay en modo Texto. Eso viene automáticamente)
194 Capı́tulo 16: Tu fichero .emacs
extensión ‘.c’ o ‘.h’ entonces Emacs cambia al modo C. También, Emacs parece
al principio una lı́nea no blanca del fichero; si la lı́nea dice ‘-*- C -*-’, Emacs
cambia al modo C. Emacs posee una lista de extensiones y especificaciones que usa
automáticamente. Además, Emacs se ve cerca de la última página por buffer, “lista
variables locales”.
Mira las secciones “Cómo los Modos Mayores son Elegidos” y “Variables Locales
en Fichero” en El Manual GNU Emacs.
Ahora, regresa al fichero .emacs.
Aquı́ está la lı́nea de nuevo; ¿cómo funciona?
(setq major-mode ’text-mode)
Esta lı́nea es un resumen, pero completa la expresión Emacs Lisp.
Ya estamos familiarizados con setq. Eso asigna la siguiente variable,
major-mode, al subsiguiente valor, que es text-mode. La marca de cita simple
antes de text-mode cuenta a Emacs como tratar directamente con el sı́mbolo, no
con cualquier cosa que pudiera existir. Véase Sección 1.9 “Configurando el Valor
de una Variable”, página 16, por un recuerdo de como setq funciona. El principal
punto es que no hay diferencia entre el procedimiento que se usa para asignar un
valor en su fichero .emacs y el procedimiento que se usa en cualquier lugar más en
Emacs.
Aquı́ está la siguiente lı́nea:
(add-hook ’text-mode-hook ’turn-on-auto-fill)
En esta lı́nea, el comando add-hook a~ nade turn-on-auto-fill para la variable.
¡turn-on-auto-fill es el nombre de un programa, que se adivina!, cambia al
modo Auto Fill.
Cada vez que Emacs cambia al modo texto, Emacs ejecuta el comando ‘hooked’
dentro de modo Texto. Ası́ que cada vez que Emacs cambia al modo Texto, Emacs
también cambia al modo de autoajuste.
En breve, la primera lı́nea causa a Emacs a entrar en modo Texto cuando se
edite un fichero, a menos que la extensión del nombre del fichero, una lı́nea no en
blanco, variables locales para contar a Emacs de otro modo.
El modo texto entre otras acciones, asigna la tabla de sintaxis para trabajar
adecuadamente a escritores. En modo texto, Emacs considera un apóstrofe como
parte de una palabra como una letra; pero Emacs no considera un perı́odo o un
espacio como parte de una palabra. De este modo, M-f se mueve hacia tı́ a través
de ‘eso’. Por otro lado, en modo C, M-f para solo después del ‘t’ de ‘eso’.
La segunda lı́nea causa que Emacs active el modo Auto Fill cuando cambia al
modo Texto. En modo Auto Fill, Emacs automáticamente rompe una lı́nea que es
demasiado amplio y trae la parte excesivamente amplia de la lı́nea de debajo a la
siguiente lı́nea. Emacs rompe lı́neas entre palabras con ellas.
Cuando el modo Auto Fill está desactivado, las lı́neas continúan a la derecha
como se escriben. Dependiendo de como configuras el valor de truncate-lines, las
palabras que se escribe si desaparecen al lado derecho de la pantalla, o lo demás son
mostradas, en un modo feo e ilegible, como una lı́nea de continuación en la pantalla.
Sección 16.7: Algunos atajos 197
Esto también muestra como configurar una tecla globalmente, para todo los
modos
Estas tres cosas, las marcas de dobles comillas, la barra invertida antes de la ‘C’,
y la marca de comilla simple son partes necesarias de atajos de teclado que tiendo
a olvidar. Afortunadamente, he llegado a recordar que mirarı́a mi fichero .emacs
existente, y lo adaptarı́a a lo que hay.
Como para el atajo en sı́: C-c w, combina la tecla prefija, C-c, con un caracter
simple, en este caso, w. Este conjunto de teclas, C-c seguido por un caracter simple,
es estrictamente reservado para un uso propio individual. (Esto se llama teclas
‘propias’, puesto que estas son para su propio uso). Siempre serı́a capaz de crear tal
atajo para el uso propio sin pisar fuerte en algún atajo más. Si siempre se escribe
una extensión a Emacs, por favor, evite tomar cualquiera de estas teclas para uso
público. Se cree que una tecla como C-c C-w en vez de eso. De otra manera, ejecutará
sin sus ‘propias’ teclas.
El comando occur muestra todas las lı́neas en el buffer actual que contiene un
emparejamiento para una expresión regular. Asociar las lı́neas que se muestran en un
búffer llamado *Occur*. Este buffer sirve como un menu para saltar a ocurrencias.
16.10 Autoloading
En vez de instalar una función cargando el fichero que lo contiene, o evaluando la
definición de función, se puede hacer la función disponible pero actualmente no se
instala hasta la primera vez llamada. Este proceso se llama autocarga (autoloading).
Cuando se ejecuta una función de autocarga, Emacs automáticamente evalúa el
fichero que contiene la definición, y entonces llama a la función.
Emacs empieza rápido con funciones de autocarga, puesto que sus librerı́as no
se cargan bien; pero si necesita esperar un momento cuando su primer uso tal como
una función, mientras que el fichero que lo contiene se evalúa.
Raramente las funciones usadas son frecuentemente autocargadas. La librerı́a
loaddefs.el coniene cientos de funciones autocargadas, desde bookmark-set a
wordstar-mode. Si se usa una función ‘rara’ frecuentemente, se deberı́a cargar este
fichero de función con una expresión de load en tu fichero .emacs.
En mi fichero .emacs, se cargan 14 librerı́as que contienen funciones que de
otro modo serı́an autocargadas. (Actualmente, eso habrı́a sido mejor para incluir
estos ficheros en mi Emacs ‘volcado’, pero se olvida. Véase Sección “Construyendo
Emacs” in El Manual de Referencia GNU Emacs Lisp, y el fichero INSTALL para
más acerca de volcados.)
Se puede también querer incluir expresiones autocargadas en tu fichero .emacs.
autoload es una función construida que toma cinco argumento, los tres finales
de los que son opcionales. El primer argumento es el nombre de la función para
ser autocargada. El segundo es el nombre del fichero para ser cargado. El tercer
argumento es documentación para la función, y el cuarto cuenta si la función puede
ser llamada interactivmente. El quinto argumento cuenta que tipo de objeto —
autoload puede manejar un mapa de teclado o macro tan bien como una función
(por defecto es una función).
Aquı́ hay un ejemplo tı́pico:
(autoload ’html-helper-mode
"html-helper-mode" "Editar documentos HTML" t)
(html-helper-mode es una vieja alternativa a html-mode, que es una parte estándar
de la distribución.)
Esta expresión autocarga la función html-helper-mode. Esto se toma desde el fiche-
ro html-helper-mode-el (o desde la versión compilada html-helper-mode.elc, si
eso existe). El fichero debe ser localizado en un directorio especı́fico por load-path.
La documentación dice que esto es un modo para ayudar a editar documentos
escritos en Lenguaje de Marcas de Hiper Texto. Se puede llamar este modo interac-
tivamente escribiendo M-x html-helper-mode. (Se necesitan duplicar las funciones
regulares de documentación en la expresión de autocarga porque la función regular
no está todavı́a cargada, ası́ su documentación no está disponible.)
Véase Sección “Autocarga” in El Manual de Referencia de GNU Emacs Lisp,
para más información.
202 Capı́tulo 16: Tu fichero .emacs
2
Cuando se empiezan las instancias de Emacs que no cargan mi fichero .emacs o cual-
quier fichero, también se puede deshabilitar la ocultación:
emacs -q --no-site-file -eval ’(blink-cursor-mode nil)’
O ahora,
usando un conjunto más sofisticado de opciones, emacs -Q - D
Sección 16.12: Colores X11 203
3
también se ejecutan gestores de ventanas más modernos, tales como Enlightenment,
Gnome, o KDE; en estos casos, con frecuencia se especifica una imagen en vez de un
color plano.
Sección 16.13: Configuraciones misceláneas para un fichero .emacs 205
Para un fichero .xinitrc o un fichero .Xsession cuando la tecla Caps Lock es que
tan lejos de la fila del home:
# Asocia la tecla etiquetada ‘Caps Lock’ a ‘Control’
# (Tal como un interfaz de usuario roto sugiere que el teclado hecho
# piensa que los ordenadores son máquinas de escribir desde 1885.)
#(" %[(" 0 6
(help-echo
"mouse-1: select window, mouse-2: delete others ..."))
(:eval (mode-line-mode-name))
mode-line-process
minor-mode-alist
#("%n" 0 2 (help-echo "mouse-2: widen" local-map (keymap ...)))
")%] "
(-3 . "%P")
;; "-%-"
)))
Aquı́, se redefine el mode line por defecto. La mayorı́a de las partes son desde el
original; pero yo creo unos pocos cambios. Yo asigno el formato de mode line default
ası́ como permitir varios modos, tales como Info, para sobreescribirlo.
Muchos elementos en la lista son auto-explicativos: mode-line-modified es una
variable que cuenta si el búffer ha sido modificado, mode-name cuenta el nombre del
modo, y ası́. Sin embargo, el formato parece complicado porque las dos funcionali-
dades no han sido discutidas.
La nueva cadena de formato tiene una sintaxis especial:
#("-" 0 1 (help-echo "mouse-1: select window, ..."))
El #( empieza una lista. El primer elemento de la lista es la cadena en sı́, solo un
‘-’. El segundo y tercer elemento especifica el rango a través del cuarto elemento
aplicado. Un rango empieza después un carácter, ası́ un cero significa el rango
que empieza solo después del primer caracter; un 1 significa que el rango finaliza
solo después del primer caracter. El tercer elemento es la propiedad para el rango.
Eso consiste en una lista de propiedades, un nombre de propiedad, en este caso,
‘help-echo’, seguido por un valor, en este caso, una cadena. El segundo, tercer y
cuarto elemento de este nuevo formato de cadena puede ser repetido.
Véase Sección “Propiedades de Texto” in El Manual de Referencia de GNU
Emacs Lisp, y ver Sección “Formato Mode Line” in El Manual de Referencia de
GNU Emacs Lisp, para más información.
mode-line-buffer-identification muestra el nombre del buffer. Eso es una
lista empezando por (#("%12b" 0 4 .... El #( empieza la lista.
El ‘"%12b"’ muestra el nombre del actual búffer, usando la función buffer-name
con la que estamos familiarizados; el ‘12’ especifica el número máximo de caracteres
que serán mostrados. Cuando un nombre tiene pocos caracteres, el espacio en blanco
se a~nade para rellenar este número. (Los nombres del búffer puede y con frecuencia
serán más largos de 12 caracteres; esta longitud funciona bien en la tı́pica ventana
de 80 columnas de ancho.)
:eval dice evaluar la siguiente forma y usa el resultado como una cadena para
mostrarse. En este caso, la expresión muestra el primer componente del sistema
completo. El fin del primer componente es un ‘.’ (‘periodo’), ası́ se usa la función
string-match para contar el tama~ no del primer componente. La subcadena desde el
caracter cero a este tama~no del primer componente. La subcadena desde el caracter
cero a este tama~no es el nombre de la máquina.
208 Capı́tulo 16: Tu fichero .emacs
Esta es la expresión:
(:eval (substring
(system-name) 0 (string-match "\\..+" (system-name))))
‘%[’ y ‘%]’ causa un par de corchetes que aparezcan por cada edición nivel de
edición recursiva editando el nivel. ‘%n’ dice ‘Encoger’ cuando esto puede hacerse.
‘%P’ te cuenta el porcentaje del búffer que está debajo de la ventana, o ‘arriba’,
‘abajo’, o ‘todo’. (Una minúscula ‘p’ cuenta el porcentaje bajo el alto de la ventana.)
‘%-’ inserta suficientes guiones para rellenar la lı́nea.
Recuerda, “No tiene que gustarte Emacs para que le gustes” — Emacs puede
tener diferentes colores, diferentes comandos, y diferentes teclas que un Emacs por
defecto.
Por otro lado, si se quiere traer un plano ‘fuera de la caja’ Emacs, sin persona-
lización, escribe:
emacs -q
Esto inicializará un Emacs que no cargue tu ~/.emacs fichero de inicialización. Uno
plano, el que trae Emacs por defecto. Nada más.
Sección 17.1: depurar 209
17 Depurando
GNU Emacs tiene dos depuradores, debug y edebug. El primero es construido dentro
de las tripas de Emacs y está siempre contigo; el segundo requiere que exista una
función antes de que se pueda usar.
Ambos depuradores son descritos extensivamente en Sección “Depurando Pro-
gramas Lisp” in El Manual de Referencia GNU Emacs Lisp. En este capı́tulo, se
explicará un breve ejemplo de esto.
17.1 depurar
Supón que se ha escrito una definición de función que se pretende devolver la suma
de los números 1 a través de un número dado. (Esta es la función triangle discu-
tida pronto. Véase “Ejemplo con Contador de Decremento”, página 118, para una
discusión.)
Sin embargo, tu definición de función tiene un error. Se ha malescrito ‘1=’ por
‘1-’. Aquı́ está la definición rota:
(defun triangle-bugged (number)
"Devuelve suma de números 1 a través de NUMBER inclusive."
(let ((total 0))
(while (> number 0)
(setq total (+ total number))
(setq number (1= number))) ; Error aquı́.
total))
Si se está leyendo esto en Info, se puede evaluar esta definición en el modo
normal. Se verá que triangle-bugged aparece en el área echo.
Ahora evalúa la función triangle-bugged con un argumento de 4:
(triangle-bugged 4)
En un GNU Emacs reciente, se creará e introducirá un búffer *Backtrace* que
dice:
---------- Buffer: *Backtrace* ----------
Debugger entered--Lisp error: (void-function 1=)
(1= number)
(setq number (1= number))
(while (> number 0) (setq total (+ total number))
(setq number (1= number)))
(let ((total 0)) (while (> number 0) (setq total ...)
(setq number ...)) total)
triangle-bugged(4)
eval((triangle-bugged 4))
eval-last-sexp-1(nil)
eval-last-sexp(nil)
call-interactively(eval-last-sexp)
---------- Buffer: *Backtrace* ----------
(Se ha reformateado este ejemplo ligeramente; el depurador no contiene muchas
lı́neas. Ası́, se puede salir del depurador escribiendo q en el buffer *Backtrace*.)
En la práctica, debido a un error tan simple como este, la lı́nea de ‘error Lisp’
explica lo que se necesita saber para corregir la definición. La función 1= está ‘vacı́a’.
210 Capı́tulo 17: Depurando
Sin embargo, si no se conoce con bastante certeza lo que está pasando, se puede
leer la traza completa.
En este caso, se necesita ejecutar una versión reciente de GNU Emacs, que au-
tomáticamente empieza el depurador que pone en el búffer *Backtrace*; o además,
se necesita para empezar el depurador manualmente como se describe debajo.
Lee el búffer *Backtrace* de abajo a arriba; eso cuenta lo que le hizo a Emacs
tener un error. Emacs hace una llamada interactiva a C-x C-e (eval-last-sexp),
que lleva a la evaluación de la expresión triangle-bugged. Cada lı́nea de debajo
cuenta lo que el intérprete Lisp evaluó.
La tercera lı́nea desde lo alto del búffer es
(setq number (1= number))
Emacs intentó evaluar esta expresión; para hacerlo ası́, se intentó evaluar la expre-
sión interna para ser mostrada en la segunda lı́nea desde arriba:
(1= number)
Aquı́ es donde el error ocurre; como se dice en la lı́nea de arriba:
Debugger entered--Lisp error: (void-function 1=)
Se puede corregir el error, reevalúa la definición de función, y entonces se puede
testear de nuevo.
17.2 debug-on-entry
Un GNU Emacs actual abre el depurador automáticamente cuando la función tiene
un error.
Incidentalmente, se puede empezar el depurador manualmente para todas las
versiones de Emacs; la ventaja es que el depurador se ejecuta incluso si no se tiene
un error en su código. Algunas veces, ¡su código estará libre de errores!
Se puede introducir el depurador cuando se llama a la función llamando
debug-on-entry.
Tipo:
M-x debug-on-entry RET triangle-bugged RET
Ahora, evalúa lo siguiente:
(triangle-bugged 5)
Todas las versiones de Emacs crearán un búffer *Backtrace* y cuenta tu que eso
es el principio para evaluar la función triangle-bugged:
---------- Buffer: *Backtrace* ----------
Debugger entered--entering a function:
* triangle-bugged(5)
eval((triangle-bugged 5))
eval-last-sexp-1(nil)
eval-last-sexp(nil)
call-interactively(eval-last-sexp)
---------- Buffer: *Backtrace* ----------
En el búffer *Backtrace*, escribe d. Emacs evaluará la primera expresión en
triangle-bugged; el búffer se parece a esto:
Sección 17.3: debug-on-quit y (debug) 211
18 Conclusión
La función current-kill es usada por yank y por yank-pop. Aquı́ está el código
para current-kill:
(defun current-kill (n &optional do-not-move)
"Rota el punto de pegue por N lugares, y entonces devuelve lo cortado.
Si N es cero, ‘interprogram-paste-function’ se asigna, y si se llama
devuelve una cadena, entonces esta cadena se a~nade al frente del
anillo de la muerte kill ring y devuelve el último corte.
Si el argumento opcional DO-NOT-MOVE es no nulo, entonces no muevas el
punto de pegue; solo devuelve el Nth corte hacia adelante.
(let ((interprogram-paste (and (= n 0)
interprogram-paste-function
(funcall interprogram-paste-function)))))
(if interprogram-paste
(progn
;; Deshabilita el programa de la función de corte cuando se
;; a~nade el nuevo texto al anillo de la muerte kill ring,
;; ası́ Emacs no intenta poseer la selección
;; con idéntico texto.
(let ((interprogram-cut-function nil))
(kill-new interprogram-paste))
interprogram-paste)
(or kill-ring (error "Kill ring is empty"))
(let ((ARGth-kill-element
(nthcdr (mod (- n (length kill-ring-yank-pointer))
(length kill-ring))
kill-ring)))
(or do-not-move
(setq kill-ring-yank-pointer ARGth-kill-element))
(car ARGth-kill-element)))))
Recuerde también que la función kill-new asigna kill-ring-yank-pointer al
último elemento del anillo de la muerte kill ring, que significa que todas las funciones
lo llaman y asigna el valor de manera indirecta: kill-append, copy-region-as-
kill, kill-ring-save, kill-line y kill-region.
Aquı́ está la lı́nea en kill-new, que se explica en la “La función kill-new”,
página 94.
(setq kill-ring-yank-pointer kill-ring)
La función current-kill parece compleja, pero usual, eso puede ser compren-
dido tomándolo aparte pieza por pieza. Primero mı́ralo en la forma esquelética:
(defun current-kill (n &optional do-not-move)
"Rota el punto a pegar por N lugares, y entonces devuelve el texto cortado."
(let varlist
body...)
Esta función tiene dos argumentos, uno es opcional. Hay una cadena de docu-
mentación. No es una función interactiva.
El cuerpo de la definición de función es una expresión let, que por sı́ misma
tiene tanto un cuerpo como una varlist.
La expresión let declara una variable que será solo usable con las asociaciones
de esta función. Esta variable se llama interprogram-paste y se copia a otro
programa. No se copia con esta instancia de GNU Emacs. La mayorı́a de los sistemas
Sección B.1: La función current-kill 221
B.2 pegar
Después de aprender acerca de current-kill, el código para la función yank es
casi fácil.
La función yank no usa la variable kill-ring-yank-pointer directamente.
Eso llama a insert-for-yank que llama a current-kill que asigna la variable
kill-ring-yank-pointer.
224 Apéndice B: Manejando el anillo de la muerte
B.3 yank-pop
Después de comprender yank y current-kill, se conoce como enfocar la función
yank-pop. Dejando fuera la documentación para guardar el espacio, se parece a
esto:
(defun yank-pop (&optional arg)
"..."
(interactive "*p")
(if (not (eq last-command ’yank))
(error "El comando previo no fué un corte"))
(setq this-command ’yank)
(unless arg (setq arg 1))
(let ((inhibit-read-only t)
(before (< (point) (mark t))))
(if before
(funcall (or yank-undo-function ’delete-region) (point) (mark t))
(funcall (or yank-undo-function ’delete-region) (mark t) (point)))
(setq yank-undo-function nil)
(set-marker (mark-marker) (point) (current-buffer))
(insert-for-yank (current-kill arg))
;; Asigna la ventana a volver donde estaba el comando yank,
;; si es posible
(set-window-start (selected-window) yank-window-start t)
(if before
;; Esto es como exchange-point-and-mark,
;; pero no activa la marca.
;; Es limpio evitar la activación, incluso aunque el comando
;; desactivase la marca porque se insertara el texto.
(goto-char (prog1 (mark t)
(set-marker (mark-marker)
(point)
(current-buffer))))))
nil)
Los ejes impresos ayudan a comprender un grafo. Para crear escalas. En un capı́tulo
anterior (véase Capı́tulo 15 “Leyendo un grafo”, página 182), se escribió el código
para imprimir el cuerpo de un grafo. Aquı́ se escribe el código para imprimir y
etiquetar ejes horizontales y verticales, a lo largo del cuerpo en sı́.
Puesto que las inserciones rellenan un búffer a la derecha y debajo del punto,
el nuevo grafo imprime la función que primero imprimirı́a el eje vertical Y, después
el cuerpo del grafo, y finalmente el eje horizontal X. Esta secuencia nos da los
contenidos de la función:
1. Configura código.
2. Imprime el eje Y.
3. Imprime el cuerpo del grafo.
4. Imprime el eje X.
5 -
1 -
La función se pasarı́a a lo alto del grafo, y ası́ construyen e insertan los números y
marcas apropiados.
Es suficientemente fácil ver en la figura que la etiqueta del eje Y pero se puede
decir en palabras, y entonces escribir una definición de función para hacer el trabajo
es otra materia. No es bastante verdad decir que se quiere un número y un tic cada
cinco lı́neas: solo hay tres lı́neas entre el ‘1’ y el ‘5’ (lı́neas 2, 3 y 4), pero cuatro
lı́neas entre el ‘5’ y el ‘10’ (lı́neas 6, 7, 8 y 9). Es mejor decir que se quiere un número
y un tic en la quinta lı́nea desde abajo a cada lı́nea que es un múltiplo de cinco.
La siguiente cuestión es a que altura se etiquetarı́a. Supón que la máxima altura
de la columna mayor del grafo es siete. La etiqueta superior en el eje Y serı́a ‘5 -’,
¿y el grafo se pegarı́a debajo de la etiqueta?, ¿o la etiqueta superior serı́a ‘7 -’, y
marcar la vertical del grafo? ¿o serı́a la etiqueta superior 10 -, que es múltiplo de
cinco, y es superior al valor más alto del grafo?
La última forma es preferida. La mayorı́a de los grafos son rectángulos cuyos
lados son un número integral de pasos a lo largo — 5, 10, 15, y ası́ para un paso a
distancia de cinco. Pero tan pronto se decide usar un paso alto para el eje vertical,
se descubre que la expresión simple en la varlist para la altura de la computación
es errónea. La expresión es (apply ’max numbers-list). Esto devuelve la altura
precisa, no la altura máxima más de lo que es necesario para redondear el múltiplo
de cinco. Una expresión más compleja es requerida.
Como es normal en casos como este, un problema complejo llega a ser simple si
está dividido en varios problemas peque~
nos.
Primero, considere el caso cuando el valor superior del grafo es un múltiplo
integral de cinco — cuando eso es 5, 10, 15, o algún múltiplo de cinco. Se puede
usar este valor como la altura del eje Y.
Un camino simple y limpio para determinar si un número es múltiplo de cinco
se divide por cinco y mira si la división devuelve resto. Si no hay resto, el número es
un múltiplo de cinco. De este modo, siete dividido tiene un resto de dos, y siete no
es un entero múltiplo de cinco. Dicho de otra manera, recordando la escuela, cinco
entre siete es uno y me llevo dos. Sin embargo, diez entre dos, no tiene resto: diez
es un múltiplo entero de cinco.
230 Apéndice C: Un grafo con ejes etiquetados
(% 10 5)
La primera expresión devuelve 2 y la segunda expresión devuelve 0.
Para chequear si el valor devuelto es cero o algún otro número, se puede usar
la función zerop. Esta función devuelve t si su argumento debe ser un número, es
cero.
(zerop (% 7 5))
⇒ nil
(zerop (% 10 5))
⇒ t
De este modo, la siguiente expresión devolverá t si la altura del grafo es divisible
por cinco:
(zerop (% height 5))
(El valor de height, de acuerdo, puede ser encontrado desde (apply ’max
numbers-list).)
Por otro lado, si el valor de height no es un múltiplo de cinco, nosotros queremos
resetear el valor al siguiente múltiplo de cinco. Esta es la aritmética sencilla usando
funciones con las que ya se está familiarizado. Primero, se divide el valor de height
por cinco para determinar cuantas veces cinco va dentro del número. De este modo,
cinco va dentro doce veces. Si se a~ nade uno a este cociente y se multiplica por cinco,
obtendremos el valor del siguiente múltiplo de cinco que es más largo que el mayor.
Cinco va dentro de doce dos veces. A~ nade uno a dos, y multiplica por cinco; el
resultado es quince, que es el siguiente múltiplo de cinco que es mayor de doce. La
expresión Lisp para esto es:
(* (1+ (/ height 5)) 5)
Por ejemplo, si se evalúa lo siguiente, el resultado es 15:
(* (1+ (/ 12 5)) 5)
Todo a través de esta discusión, se ha estado usando ‘cinco’ como el valor para
las etiquetas espaciadas en el eje Y; pero se puede querer usar algún otro valor.
Generalmente, reemplazarı́a ‘cinco’ con una variable a la que poder asignar un valor.
El mejor nombre que puedo pensar para esta variable es Y-axis-label-spacing.
Sección C.2: La función print-Y-axis 231
...
(let* ((height (apply ’max numbers-list))
(height-of-top-line
(if (zerop (% height Y-axis-label-spacing))
height
;; else
(* (1+ (/ height Y-axis-label-spacing))
Y-axis-label-spacing)))
(symbol-width (length graph-blank))))
...
(Note que el uso de la función let*: el valor inicial de la altura es calculada una
vez por la expresión (apply ’max numbers-list) y entonces el valor resultado
de height es usado para computar su valor final. Véase “La expresión let*”,
página 142, para más acerca de let*.)
Además, en cada etiqueta, cada número es seguido por una cadena tal como
‘ - ’, que llamará al marcador Y-axis-tic. Esta variable está definida con defvar:
(defvar Y-axis-tic " - "
"La Cadena que sigue el número en una etiqueta del eje Y.")
El tama~ no de la etiqueta Y es la suma del tama~
no del eje Y y el tama~
no del
número del alto del grafo.
(length (concat (number-to-string height) Y-axis-tic)))
Este valor será calculado por la función print-graph en su varlist como full-Y-
label-width y se pasa dentro. (Note que no se pensaba en incluir esto en el varlist
cuando se propuso.)
Para crear un eje vertical completo, una marca de tic es concatenada con un
número; y los dos juntos pueden ser precedidos por uno o más espacios dependiendo
de cómo de largo es el número. La etiqueta consiste de tres partes: los espacios que
se lideran (opcional), el número, y la marca tic. La función se pasa al valor del
número para la fila especı́fica, y el valor del ancho de la lı́nea de arriba, que es
calculada (solo una vez) por print-graph.
(defun Y-axis-element (number full-Y-label-width)
"Construye una etiqueta NUMERADA
Un elemento numerado se parece a esto ‘ 5 - ’,
y está tan acu~
nado como se necesita ası́ todo se
alinea con el elemento para el número mayor."
(let* ((leading-spaces
(- full-Y-label-width
(length
(concat (number-to-string number)
Y-axis-tic)))))
(concat
(make-string leading-spaces ? )
(number-to-string number)
Y-axis-tic)))
La función Y-axis-element concatena junto los espacios que se lideran si cual-
quiera; el número, como una cadena; y la marca tic.
Para imaginarnos cuantos espacios guı́a la etiqueta necesita, la función sustrae
el tama~no de la etiqueta — el tama~
no del número más el tama~ no de la marca tic
— desde el ancho de la etiqueta deseada.
Los espacios en blanco se insertan usando la función make-string. Esta función
tiene dos argumentos: lo primero cuenta como de larga será a cadena y el segundo
es un sı́mbolo para el caracter a insertar, en un formato espcial. El formato es una
marca de pregunta seguida por un espacio en blanco, como este, ‘?’. Véase Sección
“Tipo de Caracter” in El Manual de Referencia Emacs Lisp, para una descripción
de la sintaxis para caracteres. (De acuerdo, se podrı́a querer reemplazar el espacio
en blanco por algún otro caracter . . . . Tu sabes qué hacer.)
La función number-to-string es usada en la expresión de concatenación, para
convertir el número a una cadena que es concatenada con los espacios que se lideran
y la marca de tic.
Sección C.2: La función print-Y-axis 233
;; tic-width
...
(* symbol-width X-axis-label-spacing)
;; number-of-X-ticks
(if (zerop (% (X-length tic-width)))
(/ (X-length tic-width))
(1+ (/ (X-length tic-width))))
Todo esto lidera directamente a la función para imprimir el eje X:
(defun print-X-axis-tic-line
(number-of-X-tics X-axis-leading-spaces X-axis-tic-element)
"Imprime ticks para el eje X."
(insert X-axis-leading-spaces)
(insert X-axis-tic-symbol) ; En la primera columna.
;; Inserta el segundo tic en el lugar adecuado.
(insert (concat
(make-string
(- (* symbol-width X-axis-label-spacing)
;; Inserta el espacio en blanco al segundo sı́mbolo tic.
(* 2 (length X-axis-tic-symbol)))
? )
X-axis-tic-symbol))
;; Inserta los ticks que permanecen.
(while (> number-of-X-tics 1)
(insert X-axis-tic-element)
(setq number-of-X-tics (1- number-of-X-tics))))
La lı́nea de números es igualmente simple:
Primero, creamos un elemento numerado con espacios en blanco antes de cada
número:
(defun X-axis-element (number)
"Construye un elemento del eje X numerado."
(let ((leading-spaces
(- (* symbol-width X-axis-label-spacing)
(length (number-to-string number)))))
(concat (make-string leading-spaces ? )
(number-to-string number))))
Lo siguiente, se crea la función para imprimir la lı́nea numerada, empezando con
el número “1” para la primera columna:
Sección C.3: La función print-X-axis 237
(defun print-X-axis-numbered-line
(number-of-X-tics X-axis-leading-spaces)
"Imprime la lı́neas de números del eje X"
(let ((number X-axis-label-spacing))
(insert X-axis-leading-spaces)
(insert "1")
(insert (concat
(make-string
;; Inserta espacios en blanco al siguiente número.
(- (* symbol-width X-axis-label-spacing) 2)
? )
(number-to-string number)))
;; Insertar números.
(setq number (+ number X-axis-label-spacing))
(while (> number-of-X-tics 1)
(insert (X-axis-element number))
(setq number (+ number X-axis-label-spacing))
(setq number-of-X-tics (1- number-of-X-tics)))))
| | | | |
1 5 10 15 20
Los valores para la máxima altura del grafo y el ancho de un sı́mbolo se computan
por print-graph es su expresión let; ası́ graph-body-print debe ser cambiado
para aceptarlos.
(let (from-position)
(while numbers-list
(setq from-position (point))
(insert-rectangle
(column-of-graph height (car numbers-list)))
(goto-char from-position)
(forward-char symbol-width)
;; Dibuja el grafo columna por columna.
(sit-for 0)
(setq numbers-list (cdr numbers-list)))
;; Posiciona el punto para las etiquetas del eje X.
(forward-line height)
(insert "\n")))
(print-Y-axis
height-of-top-line full-Y-label-width vertical-step)
(graph-body-print
numbers-list height-of-top-line symbol-width)
(print-X-axis numbers-list)))
Sección C.4: Imprimiendo el grafo completo 241
*
** *
5 - **** *
**** ***
* *********
************
1 - *************
| | | |
1 5 10 15
Por otro lado, si se pasa a print-graph un vertical-step valor de 2, evaluando
esta expresión:
(print-graph ’(3 2 5 6 7 5 3 4 6 4 3 2 1) 2)
El grafo se parece a esto:
20 -
*
** *
10 - **** *
**** ***
* *********
************
2 - *************
| | | |
1 5 10 15
(Una pregunta: ¿es el ‘2’ debajo del eje vertical un error o una funcionalidad? Si
se piensa que es un error, y serı́a un ‘1’, (o incluso un ‘0’), se pueden modificar las
fuentes.)
242 Apéndice C: Un grafo con ejes etiquetados
(multiply-by-seven 3)
function argument
Esta expresión devuelve 2. El 100 es pasado para la función, que divide este número
por 50.
244 Apéndice C: Un grafo con ejes etiquetados
(defun print-X-axis-numbered-line
(number-of-X-tics X-axis-leading-spaces
&optional horizontal-step)
"Imprime la lı́neas de números X-axis"
(let ((number X-axis-label-spacing)
(horizontal-step (or horizontal-step 1)))
(insert X-axis-leading-spaces)
;; Elimina espacios extra de guı́a.
(delete-char
(- (1-
(length (number-to-string horizontal-step)))))
(insert (concat
(make-string
;; Inserta espacio en blanco.
(- (* symbol-width
X-axis-label-spacing)
(1-
(length
(number-to-string horizontal-step)))
2)
? )
(number-to-string
(* number horizontal-step))))
;; Insertar los números que permanecen.
(setq number (+ number X-axis-label-spacing))
(while (> number-of-X-tics 1)
(insert (X-axis-element
(* number horizontal-step)))
(setq number (+ number X-axis-label-spacing))
(setq number-of-X-tics (1- number-of-X-tics)))))
246 Apéndice C: Un grafo con ejes etiquetados
Si se está leyendo esto en Info, se pueden ver las nuevas versiones print-X-axis
y print-graph y los evaluarlas. Si se está leyendo esto en un libro impreso, se
pueden ver las lı́neas cambiadas aquı́ (el texto completo es mucho para imprimir).
(defun print-X-axis (numbers-list horizontal-step)
...
(print-X-axis-numbered-line
tic-number leading-spaces horizontal-step))
(defun print-graph
(numbers-list
&optional vertical-step horizontal-step)
...
(print-X-axis numbers-list horizontal-step))
Sección C.4: Imprimiendo el grafo completo 247
1000 - *
**
**
**
**
750 - ***
***
***
***
****
500 - *****
******
******
******
*******
250 - ********
********* *
*********** *
************* *
50 - ***************** * *
| | | | | | | |
10 50 100 150 200 250 300 350
Pero hay una razón particular de por qué la libertad de modificar es crucial
para la documentación de software libre. Cuando las personas ejercita su derecho
a modificar el software, y a~ nadir o cambiar sus funcionalidades, si son consciente
ellos cambiarán el manual también — ası́ se puede proveer documentación usable y
cuidada con el programa modificado. Un manual que prohibe a los programadores
ser consciente y finalizar el trabajo, o más precisamente requiere escribir un nuevo
manual desde cero si ellos cambian el programa, no se ajusta a las necesidades de
nuestra comunidad.
Mientras una serie de prohibiciones en la modificación es inaceptable, algunos
tipos de lı́mites en el método de modificar no tiene tanto problema. Por ejemplo,
los requisitos para preservar la noticia de autores del copyright, los términos de
distribución, o la lista de autores, estén ok. Eso es también no da problemas para
requerir versiones modificadas para incluir notificar que fueron modificadas, incluso
tienen secciones enteras que puede no ser eliminadas o cambiadas, tan largo como
estas secciones tratan con asuntos no técnicos. (Algunos manuales de GNU los
tienen).
Estos tipos de restricciones no son un problema porque, como materia práctica,
no para al programador consciente desde la adaptación del manual para ajustar el
programa modificado. En otras palabras, no se bloquea la comunidad del software
libre haciendo el uso completo del manual.
Sin embargo, debe ser posible modificar todo el contenido técnico del manual, y
entonces se distribuye el resultado en todos los medios usuales, a través de todos los
canales usuales; de otro modo, las restricciones bloquean la comunidad, el manual
no es libre, y ası́ no se necesita otro manual.
Desafortunadamente, con frecuencia es duro encontrar a alguien a escribir otro
manual cuando un manual privativo. El obstáculo es que muchos usuario piensan
que un manual privativo es suficientemente bueno — ası́ ellos no ven la necesidad
de escribir un manual libre. Ellos no ven que el sistema operativo tiene un gazapo
que necesita se rellenado.
¿Por qué los usuarios piensan que los manuales privativos son suficientemente
buenos? Algunos no han considerado la cuestión. Espero que este artı́culo hará
alguna cosa para cambiar esto.
Otros usuarios considera manuales privativos aceptables para la misma razón ası́
muchas personas software privativo aceptable: ellos judgan en términos puramente
prácticos, no usando la liberta como un criterio. Estas personas son tituladas a
sus opiniones, pero desde que estas opciones crezcan desde valores que no incluyen
libertad, ellas no están guiadas por esto quienes valoran la libertad.
Por favor, populariza esta cuestión. Se continúa a perder manuales para publi-
cación privativa. Si se populariza que los manuales privativos no son suficientes,
quizás la siguiente persona que quiere ayudar a GNU escribiendo documentación
realizará, antes de que sea demasiado tarde, lo que él debe que todo sea libre.
Se puede también animar editoriales comerciales a vender manuales libres o con
copyleft en vez de uno privativo. Un camino que se puede ayudar esto chequea los
términos de la distribución de un manual antes de que se compre, y preferimos
manuales copyleft a los no copyleft.
250 Apéndice D: Software Libre y Manuales Libres
Note: La Fundación para el Software Libre mantiene una página en su sitio Web
que liste libros libres disponibles desde otras editoriales:
http://www.gnu.org/doc/other-free-books.html
251
The “Invariant Sections” are certain Secondary Sections whose titles are de-
signated, as being those of Invariant Sections, in the notice that says that the
Document is released under this License. If a section does not fit the above
definition of Secondary then it is not allowed to be designated as Invariant.
The Document may contain zero Invariant Sections. If the Document does not
identify any Invariant Sections then there are none.
The “Cover Texts” are certain short passages of text that are listed, as Front-
Cover Texts or Back-Cover Texts, in the notice that says that the Document
is released under this License. A Front-Cover Text may be at most 5 words,
and a Back-Cover Text may be at most 25 words.
A “Transparent” copy of the Document means a machine-readable copy, re-
presented in a format whose specification is available to the general public,
that is suitable for revising the document straightforwardly with generic text
editors or (for images composed of pixels) generic paint programs or (for dra-
wings) some widely available drawing editor, and that is suitable for input to
text formatters or for automatic translation to a variety of formats suitable
for input to text formatters. A copy made in an otherwise Transparent file
format whose markup, or absence of markup, has been arranged to thwart or
discourage subsequent modification by readers is not Transparent. An image
format is not Transparent if used for any substantial amount of text. A copy
that is not “Transparent” is called “Opaque”.
Examples of suitable formats for Transparent copies include plain ascii wit-
hout markup, Texinfo input format, LaTEX input format, SGML or XML using
a publicly available DTD, and standard-conforming simple HTML, PostScript
or PDF designed for human modification. Examples of transparent image for-
mats include PNG, XCF and JPG. Opaque formats include proprietary formats
that can be read and edited only by proprietary word processors, SGML or
XML for which the DTD and/or processing tools are not generally available,
and the machine-generated HTML, PostScript or PDF produced by some word
processors for output purposes only.
The “Title Page” means, for a printed book, the title page itself, plus such
following pages as are needed to hold, legibly, the material this License requires
to appear in the title page. For works in formats which do not have any title
page as such, “Title Page” means the text near the most prominent appearance
of the work’s title, preceding the beginning of the body of the text.
The “publisher” means any person or entity that distributes copies of the
Document to the public.
A section “Entitled XYZ” means a named subunit of the Document whose
title either is precisely XYZ or contains XYZ in parentheses following text
that translates XYZ in another language. (Here XYZ stands for a specific
section name mentioned below, such as “Acknowledgements”, “Dedications”,
“Endorsements”, or “History”.) To “Preserve the Title” of such a section when
you modify the Document means that it remains a section “Entitled XYZ”
according to this definition.
253
The Document may include Warranty Disclaimers next to the notice which
states that this License applies to the Document. These Warranty Disclaimers
are considered to be included by reference in this License, but only as regards
disclaiming warranties: any other implication that these Warranty Disclaimers
may have is void and has no effect on the meaning of this License.
2. VERBATIM COPYING
You may copy and distribute the Document in any medium, either commer-
cially or noncommercially, provided that this License, the copyright notices,
and the license notice saying this License applies to the Document are repro-
duced in all copies, and that you add no other conditions whatsoever to those
of this License. You may not use technical measures to obstruct or control
the reading or further copying of the copies you make or distribute. However,
you may accept compensation in exchange for copies. If you distribute a large
enough number of copies you must also follow the conditions in section 3.
You may also lend copies, under the same conditions stated above, and you
may publicly display copies.
3. COPYING IN QUANTITY
If you publish printed copies (or copies in media that commonly have printed
covers) of the Document, numbering more than 100, and the Document’s li-
cense notice requires Cover Texts, you must enclose the copies in covers that
carry, clearly and legibly, all these Cover Texts: Front-Cover Texts on the front
cover, and Back-Cover Texts on the back cover. Both covers must also clearly
and legibly identify you as the publisher of these copies. The front cover must
present the full title with all words of the title equally prominent and visible.
You may add other material on the covers in addition. Copying with changes
limited to the covers, as long as they preserve the title of the Document and
satisfy these conditions, can be treated as verbatim copying in other respects.
If the required texts for either cover are too voluminous to fit legibly, you
should put the first ones listed (as many as fit reasonably) on the actual cover,
and continue the rest onto adjacent pages.
If you publish or distribute Opaque copies of the Document numbering more
than 100, you must either include a machine-readable Transparent copy along
with each Opaque copy, or state in or with each Opaque copy a computer-
network location from which the general network-using public has access to
download using public-standard network protocols a complete Transparent
copy of the Document, free of added material. If you use the latter option,
you must take reasonably prudent steps, when you begin distribution of Opa-
que copies in quantity, to ensure that this Transparent copy will remain thus
accessible at the stated location until at least one year after the last time you
distribute an Opaque copy (directly or through your agents or retailers) of that
edition to the public.
It is requested, but not required, that you contact the authors of the Document
well before redistributing any large number of copies, to give them a chance to
provide you with an updated version of the Document.
254 Apéndice E: GNU Free Documentation License
4. MODIFICATIONS
You may copy and distribute a Modified Version of the Document under the
conditions of sections 2 and 3 above, provided that you release the Modified
Version under precisely this License, with the Modified Version filling the role
of the Document, thus licensing distribution and modification of the Modified
Version to whoever possesses a copy of it. In addition, you must do these things
in the Modified Version:
A. Use in the Title Page (and on the covers, if any) a title distinct from that
of the Document, and from those of previous versions (which should, if
there were any, be listed in the History section of the Document). You
may use the same title as a previous version if the original publisher of
that version gives permission.
B. List on the Title Page, as authors, one or more persons or entities respon-
sible for authorship of the modifications in the Modified Version, together
with at least five of the principal authors of the Document (all of its prin-
cipal authors, if it has fewer than five), unless they release you from this
requirement.
C. State on the Title page the name of the publisher of the Modified Version,
as the publisher.
D. Preserve all the copyright notices of the Document.
E. Add an appropriate copyright notice for your modifications adjacent to
the other copyright notices.
F. Include, immediately after the copyright notices, a license notice giving
the public permission to use the Modified Version under the terms of this
License, in the form shown in the Addendum below.
G. Preserve in that license notice the full lists of Invariant Sections and re-
quired Cover Texts given in the Document’s license notice.
H. Include an unaltered copy of this License.
I. Preserve the section Entitled “History”, Preserve its Title, and add to it
an item stating at least the title, year, new authors, and publisher of the
Modified Version as given on the Title Page. If there is no section Entitled
“History” in the Document, create one stating the title, year, authors, and
publisher of the Document as given on its Title Page, then add an item
describing the Modified Version as stated in the previous sentence.
J. Preserve the network location, if any, given in the Document for public
access to a Transparent copy of the Document, and likewise the network
locations given in the Document for previous versions it was based on.
These may be placed in the “History” section. You may omit a network
location for a work that was published at least four years before the
Document itself, or if the original publisher of the version it refers to
gives permission.
K. For any section Entitled “Acknowledgements” or “Dedications”, Preserve
the Title of the section, and preserve in the section all the substance
255
unique number. Make the same adjustment to the section titles in the list of
Invariant Sections in the license notice of the combined work.
In the combination, you must combine any sections Entitled “History” in the
various original documents, forming one section Entitled “History”; likewise
combine any sections Entitled “Acknowledgements”, and any sections Entitled
“Dedications”. You must delete all sections Entitled “Endorsements.”
6. COLLECTIONS OF DOCUMENTS
You may make a collection consisting of the Document and other documents
released under this License, and replace the individual copies of this License
in the various documents with a single copy that is included in the collection,
provided that you follow the rules of this License for verbatim copying of each
of the documents in all other respects.
You may extract a single document from such a collection, and distribute it
individually under this License, provided you insert a copy of this License into
the extracted document, and follow this License in all other respects regarding
verbatim copying of that document.
7. AGGREGATION WITH INDEPENDENT WORKS
A compilation of the Document or its derivatives with other separate and in-
dependent documents or works, in or on a volume of a storage or distribution
medium, is called an “aggregate” if the copyright resulting from the compila-
tion is not used to limit the legal rights of the compilation’s users beyond what
the individual works permit. When the Document is included in an aggregate,
this License does not apply to the other works in the aggregate which are not
themselves derivative works of the Document.
If the Cover Text requirement of section 3 is applicable to these copies of the
Document, then if the Document is less than one half of the entire aggrega-
te, the Document’s Cover Texts may be placed on covers that bracket the
Document within the aggregate, or the electronic equivalent of covers if the
Document is in electronic form. Otherwise they must appear on printed covers
that bracket the whole aggregate.
8. TRANSLATION
Translation is considered a kind of modification, so you may distribute trans-
lations of the Document under the terms of section 4. Replacing Invariant Sec-
tions with translations requires special permission from their copyright holders,
but you may include translations of some or all Invariant Sections in addition
to the original versions of these Invariant Sections. You may include a trans-
lation of this License, and all the license notices in the Document, and any
Warranty Disclaimers, provided that you also include the original English ver-
sion of this License and the original versions of those notices and disclaimers.
In case of a disagreement between the translation and the original version of
this License or a notice or disclaimer, the original version will prevail.
If a section in the Document is Entitled “Acknowledgements”, “Dedications”,
or “History”, the requirement (section 4) to Preserve its Title (section 1) will
typically require changing the actual title.
257
9. TERMINATION
You may not copy, modify, sublicense, or distribute the Document except as
expressly provided under this License. Any attempt otherwise to copy, modify,
sublicense, or distribute it is void, and will automatically terminate your rights
under this License.
However, if you cease all violation of this License, then your license from a
particular copyright holder is reinstated (a) provisionally, unless and until the
copyright holder explicitly and finally terminates your license, and (b) per-
manently, if the copyright holder fails to notify you of the violation by some
reasonable means prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is reinstated perma-
nently if the copyright holder notifies you of the violation by some reasonable
means, this is the first time you have received notice of violation of this License
(for any work) from that copyright holder, and you cure the violation prior to
30 days after your receipt of the notice.
Termination of your rights under this section does not terminate the licenses
of parties who have received copies or rights from you under this License. If
your rights have been terminated and not permanently reinstated, receipt of a
copy of some or all of the same material does not give you any rights to use it.
10. FUTURE REVISIONS OF THIS LICENSE
The Free Software Foundation may publish new, revised versions of the GNU
Free Documentation License from time to time. Such new versions will be
similar in spirit to the present version, but may differ in detail to address new
problems or concerns. See http://www.gnu.org/copyleft/.
Each version of the License is given a distinguishing version number. If the
Document specifies that a particular numbered version of this License “or
any later version” applies to it, you have the option of following the terms
and conditions either of that specified version or of any later version that
has been published (not as a draft) by the Free Software Foundation. If the
Document does not specify a version number of this License, you may choose
any version ever published (not as a draft) by the Free Software Foundation.
If the Document specifies that a proxy can decide which future versions of this
License can be used, that proxy’s public statement of acceptance of a version
permanently authorizes you to choose that version for the Document.
11. RELICENSING
“Massive Multiauthor Collaboration Site” (or “MMC Site”) means any World
Wide Web server that publishes copyrightable works and also provides promi-
nent facilities for anybody to edit those works. A public wiki that anybody can
edit is an example of such a server. A “Massive Multiauthor Collaboration”
(or “MMC”) contained in the site means any set of copyrightable works thus
published on the MMC site.
“CC-BY-SA” means the Creative Commons Attribution-Share Alike 3.0 license
published by Creative Commons Corporation, a not-for-profit corporation with
258 Apéndice E: GNU Free Documentation License
Índice
C
> C, una disgresión dentro. . . . . . . . . . . . . . . 98
> (mayor que) . . . . . . . . . . . . . . . . . . . . . . . . 36 ‘cadena’ definida . . . . . . . . . . . . . . . . . . . . . . . 3
‘cadena vacı́a’ definida . . . . . . . . . . . . . . . 43
Caja con cajones, metáfora para su
A sı́mbolo . . . . . . . . . . . . . . . . . . . . . . . . . 106
Acumular, tipo de patrón recursivo . . . 130 Cajones, Caja de, metáfora para un
add-hook . . . . . . . . . . . . . . . . . . . . . . . . . . . . 196 sı́mbolo . . . . . . . . . . . . . . . . . . . . . . . . . 106
Alias de correo . . . . . . . . . . . . . . . . . . . . . . . 197 Cambiando a un búffer . . . . . . . . . . . . . . . . 23
Almacenando y cortando texto . . . . . . . . 82 Cambiando una definición de función . . 29
Ampliando . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70 cancel-debug-on-entry . . . . . . . . . . . . . 211
and . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97, 142 car, introducido . . . . . . . . . . . . . . . . . . . . . . 74
Anillo, creando un lista como un . . . . . 219 Cargando ficheros . . . . . . . . . . . . . . . . . . . . 200
append-to-buffer . . . . . . . . . . . . . . . . . . . . 50 Categorı́as de sintaxis y tablas . . . . . . . 163
apply . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 184 cdr, introducido . . . . . . . . . . . . . . . . . . . . . . 74
apropos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 182 Código de instalación . . . . . . . . . . . . . . . . . 32
Argumento como variable local . . . . . . . 120 Código Permanente de Instalación . . . . . 32
‘argumento’ definido. . . . . . . . . . . . . . . . . . . 11 ‘comando’ definido . . . . . . . . . . . . . . . . . . . . . 20
Argumento, tipo incorrecto de . . . . . . . . . 13 Comentarios en Código Lisp . . . . . . . . . . . 29
Argumentos . . . . . . . . . . . . . . . . . . . . . . . . . . . 11 Common Lisp . . . . . . . . . . . . . . . . . . . . . . . . . . 3
Argumentos de tipos datos . . . . . . . . . . . . 12 compare-windows . . . . . . . . . . . . . . . . . . . . 197
Argumentos Opcionales . . . . . . . . . . . . . . . 63 Compilando Byte . . . . . . . . . . . . . . . . . . . . . . 7
Argumentos, número variable de. . . . . . . 13 concatenar . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
Asignando tecla globalmente . . . . . . . . . 198 cond . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 128
Asignando valor de variable . . . . . . . . . . . 16 Condicional con if . . . . . . . . . . . . . . . . . . . . 36
Asignar tecla global . . . . . . . . . . . . . . . . . . 198 Condicional marcarán estas dos versiones
Asociaciones, teclas, arreglando . . . . . . 205 de Emacs . . . . . . . . . . . . . . . . . . . . . . . . 202
261
Robert J. Chassell ha trabajado con GNU Emacs desde 1985. Él escribe,
edita y ense~na Emacs y Emacs Lisp, y habla alrededor del mundo acer-
ca de la libertad del software. Chassell es Director fundador y Tesorero
de la Fundación por el Software Libre (FSF). Él se graduó la Universi-
dad de Cambridge, en Inglaterra. Él tiene un interés contínuo en historia
económica y social y vuela su propio aeroplano