Академический Документы
Профессиональный Документы
Культура Документы
Apuntes de Notxor
Contents
1 Aprendiendo Emacs Lisp
2 Prefacio
Por qu . . . . . . . .
Leyendo esto . . . . .
Para quin est escrito
Historia de Lisp . . . .
9
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
11
11
11
12
12
3 Procesamiento de listas
Listas . . . . . . . . . . . . . . . . . . . . . . . .
Nmeros, listas dentro de listas . . . . . . .
Lisp Atoms . . . . . . . . . . . . . . . . . .
Los espacios en las listas . . . . . . . . . . .
GNU Emacs te ayuda a teclear listas . . . .
Correr un programa . . . . . . . . . . . . . . . .
Generar un Error Message . . . . . . . . . . . . .
Nombres de smbolos y definicin de funciones .
El intrprete de Lisp . . . . . . . . . . . . . . . .
Complicaciones . . . . . . . . . . . . . . . .
Byte Compiling . . . . . . . . . . . . . . . .
Evaluacin . . . . . . . . . . . . . . . . . . . . .
Cmo acta el intrprete . . . . . . . . . . .
Evaluando listas internas . . . . . . . . . .
Variables . . . . . . . . . . . . . . . . . . . . . .
Ejemplo fill-column . . . . . . . . . . . .
Mensaje de error de un smbolo sin funcin
Mensaje de error de un smbolo sin valor . .
Argumentos . . . . . . . . . . . . . . . . . . . . .
Tipos de datos . . . . . . . . . . . . . . . .
Un argumento de valor de variable o lista .
Cantidad variable de argumentos . . . . . .
Utilizar el Tipo errneo como argumento . .
La funcin message . . . . . . . . . . . . .
Establecer un valor en una variable . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
13
13
13
14
14
15
15
16
17
17
17
18
18
18
18
19
19
19
20
21
21
21
22
22
23
24
. . .
. . .
esto
. . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
CONTENTS
Utilizando
Utilizando
Contar . .
Resumen . . .
Ejercicios . . .
set
setq
. . .
. . .
. . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
24
24
25
25
26
. . . .
. . . .
. . . .
. . . .
punto
. . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
27
27
28
28
28
29
29
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
31
31
31
33
33
34
34
34
35
35
36
36
36
37
37
38
38
38
39
40
40
40
41
41
42
42
45
4 Practicar la evaluacin
Cmo evaluar . . . . . . . . . . . .
Nombres de buffer . . . . . . . . .
Obteniendo Buffers . . . . . . . . .
Cambiando de Buffer . . . . . . .
Tamao de Buffer y la localizacin
Ejercicio . . . . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
. .
. .
. .
. .
del
. .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
CONTENTS
Un vistazo a mark-whole-buffer . . . . . . . .
El cuerpo de mark-whole-buffer . . . . . . . .
La definicin de append-to-buffer . . . . . . . . .
Un vistazo a append-to-buffer . . . . . . . .
La expresin interactiva de append-to-buffer
El cuerpo de append-to-buffer . . . . . . . .
save-excursion en append-to-buffer . . . .
Resumen . . . . . . . . . . . . . . . . . . . . . . . .
Ejercicios . . . . . . . . . . . . . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
49
49
50
50
51
52
52
54
55
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
57
57
58
58
59
61
62
63
8 Narrowing y Widening
Ventajas del narrowing . . . . . . . . .
La forma especial save-restriction
what-line . . . . . . . . . . . . . . .
Ejercicio con Narrowing . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
65
65
65
66
67
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
69
69
69
70
70
71
71
72
73
73
74
. . . . . . . .
. . . . . . . .
zap-to-char
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
75
75
76
76
76
77
77
78
79
.
.
.
.
.
.
.
.
.
.
.
.
CONTENTS
kill-region . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
La definicin completa de kill-region . . . . . . . . . . .
condition-case . . . . . . . . . . . . . . . . . . . . . . . .
Lisp macro . . . . . . . . . . . . . . . . . . . . . . . . . . .
copy-region-as-kill . . . . . . . . . . . . . . . . . . . . . . . .
La definicin de la funcin copy-region-as-kill completa
El cuerpo de copy-region-as-kill . . . . . . . . . . . . .
Digresin en C . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Inicializar una variable con defvar . . . . . . . . . . . . . . . . .
Ver el valor actual de una variable . . . . . . . . . . . . . .
defvar y un asterisco . . . . . . . . . . . . . . . . . . . . .
Resumen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Ejercicin de bsqueda . . . . . . . . . . . . . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
79
79
81
81
82
82
83
86
88
88
89
89
91
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
101
101
102
102
103
104
106
108
108
109
110
110
111
112
112
114
115
118
119
121
123
CONTENTS
La expresin regular sentece-end . . . . . . . . . . . . . .
La funcin re-search-forward . . . . . . . . . . . . . . . .
forward-sentence . . . . . . . . . . . . . . . . . . . . . . .
La definicin de funcin forward-sentence completa
Los bucles while . . . . . . . . . . . . . . . . . . . . .
La expresin regular de bsqueda . . . . . . . . . . . .
forward-paragraph: una mina de oro de funciones . . . . .
La definicin de forward-paragraph resumida . . . .
La expresin let* . . . . . . . . . . . . . . . . . . . .
Bucle while para moverse hacia adelante . . . . . . .
Resumen . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Ejercicios con re-search-forward . . . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
123
124
125
125
126
127
128
128
129
130
133
134
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
135
135
135
136
138
140
143
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
145
145
146
146
147
149
150
150
152
152
17 Footnotes
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
153
CONTENTS
Chapter 1
10
Chapter 2
Prefacio
La mayor parte de GNU emacs est escrito en un lenguaje de programacin
llamado Emacs Lisp. Por lo que he ledo por ah, algunas funciones bsicas
se escribieron en c, pero a esas funciones se le aade una completa capa de
funciones de ms alto nivel.
El tema es qu se puede hacer con este entorno?
Por qu
Normalmente se podra pensar que Emacs Lisp se restringe al uso del editor y
sus funciones. Pero parece que es un lenguaje completo y se puede hacer casi de
todo. Hay que darle una oportunidad.
Leyendo esto
Se encontrarn a lo largo de este documento pequeos ejemplos de cdigo. El
hecho de hacerlo en formato org es para poder ejecutar dentro de Emacs el
cdigo que se vaya encontrando y ver sus efectos dentro del editor, sin necesidad
de salir del entorno mientras se lee.
Creo que de ese modo puedo familiarizarme con cdigo que funciona y adems
con cmo funciona el propio editor. Tambin para familiarizarse con el uso de
Emacs y un conjunto de comandos que se utilizan para depurar, ejecutar cdigo,
etc. como por ejemplo, el uso de la combinacin de teclas M-. que llama al
comando find-tag.
11
12
CHAPTER 2. PREFACIO
Historia de Lisp
Lisp se desarroll a finales de la dcada de 1950 en el Massachusetts Institute of
Technology para investigar sobre Inteligencia Artificial.1
GNU Emacs Lisp se inspira en Maclisp que se escribi en el MIT en los 60.
Inspirado por el Common Lisp, que se hizo estndar en los 1980. Aunque, Emacs
Lisp es ms sencillo que Common Lisp.2
1 Un
Chapter 3
Procesamiento de listas
Para un ojo no entrenado, Lisp es un extrao lenguaje de programacin. En Lisp
el cdigo tiene parntesis por todos lados. Lisp proviene de LISt Processing y el
lenguaje de programacin maneja listas metidas entre parntesis. Los parntesis
marcan los lmites de la lista. Algunas veces una lista aparece precedida por un
apstrofe , que se llama single-quote en Lisp.1
Listas
En Lisp, una lista se parece a: \'(rose violet daisy buttercup). Esta lista
est precedida por un nico apstrofe. Tambin se puede escribir como:
'(rose
violet
daisy
buttercup)
13
14
Lisp Atoms
En Lisp, cuando decamos palabras diramos tomos. Este trmino proviene del
significado original de tomo: indivisible.
En una lista, los tomos estn separados de otros por espacios en blanco. Pero
pueden estas pegados a un parntesis.
Tcnicamente hablando, una lista en Lisp consiste en un parntesis delimitando
tomos separados por espacios o de otras listas tanto de tomos como de otras
listas. Una lista puede tener slo un tomo en ella o incluso nada. Una lista sin
nada puede aparecer como: () y se la llama lista vaca. A diferencia de lo
dems, una lista vaca es a la vez tanto un tomo como una lista.
La representacin escrita tanto de tomos como de listas se llaman symbolic
expressions (expresiones simblicas) o s-expressions. La palabra expresin
por s misma se puede referir tanto a la representacin como al tomo o a la lista
almacenados internamente en el ordenador. Frecuentemente, la gente utiliza
expresin indiscriminadamente. Tambin en muchos textos la palabra form
(forma) se utiliza como sinnimo de expresin.
En el mundo fsico hemos visto que los tomos se pueden dividir. Slo nos
precipitamos a ponerles nombres antes de averiguar su estructura interna. En
Lisp tambin hay tomos que se pueden dividir, como un array; pero el mecanismo
para hacerlo es distinto a los mecanismos para separar una lista. En lo que
concierne a las operaciones de las listas, los tomos son indivisibles.
El texto entre comillas tambin es un tomo. Por ejemplo:
'(this list includes "text between quotation marks.")
En Lisp todo el texto entrecomillado, incluyendo los signos de puntuacin y los
espacios en blanco, es un nico tomo de tipo cadena (string) y es la clase de
cosas que un ordenador puede mostrar para que un humano lo lea. Las cadenas,
por tanto, son un tipo distinto de tomo que los nmeros o los smbolos y se
usan de manera distinta.
CORRER UN PROGRAMA
15
'(this list
looks like this)
es exactamente la misma que:
'(this list looks like this)
Ambos ejemplos son la misma lista, la lista hecha con los smbolos this, list,
looks, like y this en ese orden.
Los espacios extra y los saltos de lnea se utilizan para hacer la lista ms legible
para humanos, pero hay que recordar que al menos debe haber uno.
Resumiendo: una lista est entre parntesis, una cadena entre comillas, un
smbolo parece una palabra y un nmero parece un nmero.
Correr un programa
Una lista en Lisp cualquier lista es un programa listo para correr. Si lo lanzas
lo que en la jerga Lisp se llama evaluar, el ordenador puede hacer tres cosas:
no hacer nada excepto devolver la misma lista; lanzar un mensaje de error; o,
tratar el primer smbolo como un comando que hace algo. (Normalmente la
tercera es la que queremos).
El apstrofe, que se pone en algunas listas se llama quote; cuando precede
una lista le dice a Lisp que no haga nada con ella. Pero si no hay un quote
precediendo la lista el primer tem es especial y se considera que es un comando
que el ordenador debe obedecer. En Lisp estos comandos se llaman funciones.
La lista (+ 2 2) como no tiene un apstrofe delante hace que Lisp comprenda
que + es una instruccin para hacer algo con la lista: sumar los nmeros que
siguen. Colocando el cursor detrs de la expresin en Emacs y pulsando C-x
C-e, Aparecer en el rea de eco de la aplicacin el nmero 4. Probando lo
mismo con una lista con el apstrofe delante, por ejemplo: (this is a quoted
list), aparece el texto (this is a quoted list).
En ambos casos ests proporcionando un comando al programa interno de GNU
Emacs que se llama Lisp interpreter para que evale la expresin.
16
17
El intrprete de Lisp
En lo que se ha visto hasta ahora podemos empezar a suponer qu hace el
intrprete Lisp. Primero mira si hay un quote delante de la lista; si est
devuelve la misma lista. Si no hay, el intrprete toma el primer elemento de la
lista y mira si tiene una definicin de funcin. Si la hay, el intrprete ejecuta las
instrucciones de la funcin. En otro caso muestra un mensaje de error.
As es como trabaja Lisp. Sencillo. Hay complicaciones adicionales, pero estos
son los fundamentos.
Complicaciones
Bueno, la primera complicacin. Adems de las listas, el intrprete de Lisp
puede evaluar un smbolo sin quoted y sin parntesis rodendolo. El intrprete
de Lisp intentar determinar el valor del smbolo como una variable.
La segunda complicacin ocurre porque algunas funciones son inusuales y no funcionan de la manera ordinaria. stas son las special forms o formas especiales.
Se utilizan para trabajos especiales, como definir una funcin. No son muchas.
Adems de las funciones especiales tambin hay macros. Un macro es una
construccin definida en Lisp que se diferencia de una uncin en que traduce una
expresin Lisp en otra expresin que ser la que se evale en lugar de la original.
Por el momento, no vamos a preocuparnos demasiado si es una funcin especial,
un macro o una funcin ordinaria. Por ejemplo, if es una funcin especial, pero
18
when es una macro. En las primeras versiones de Emacs, defun era una funcin
especial, pero ahora es un macro.
Byte Compiling
Otro aspecto del intrprete es que permite interpretar dos tipos de entidades: el
cdigo legible por humanos u otro ms especfico de la mquina llamado cdigo
compilado o byte compiled code. El cdigo compilado corre ms rpido que el
legible.
Se puede trasformar el cdigo legible en cdigo compilado con comandos del tipo
byte-compile-file. El cdigo compilado normalmente se almacena en ficheros
con extensin .elc en lugar de .el.
Evaluacin
Cuando el intrprete Lisp trabaja con una expresin, decimos que lo est evaluando.
VARIABLES
19
Variables
En Emacs Lisp un smbolo puede tener asignado una definicin de funcin o un
valor. Las dos son diferentes. La primera es una serie de instrucciones que el
ordenador tiene que obedecer. La segunda es algo como un nmero o un nombre,
eso puede variar (por eso se llama variable). El valor de un smbolo puede ser
un nmero, una lista o una cadena.
Un smbolo puede tener asignado tanto una definicin de funcin como un valor,
a la vez. O puede tener lo uno o lo otro. Las dos cosas estn separadas.
Ejemplo fill-column
La variable fill-column ilustra lo que es un smbolo con un valor asignado en
l. Si lo ejecutamos pulsando C-x C-e:
fill-column
En el rea de eco, Emacs muestra el valor 70. Esto es el valor que contiene. El
valor puede ser diferente, dependiendo de la configuracin del editor.
Un smbolo puede contener o, utilizando la jerga, podemos atar la variable
a un valor: a un nmero como 70; a una cadena, como es esta; a una lista
como (picea pino roble).
Un smbolo puede ser amarrado a un valor de varias maneras.
20
ARGUMENTOS
21
Argumentos
Para ver cmo se pasa la informacin a las funciones, vamos a ver nuestro viejo
ejemplo:
(+ 2 2)
Si evaluamos esa expresin aparecer el valor 4. Los nmeros sumados por + se
llaman argumentos de la funcin +.
En Lisp, los argumentos de una funcin son tomos o listas que siguen a la
funcin. Los valores devueltos al evaluar esos tomos o listas se pasan a la
funcin. Diferentes funciones necesitan diferentes cantidades de argumentos;
algunas funciones no necesitan ninguno.
Tipos de datos
El tipo de datos que se debe pasar a una funcin depende de qu clase de
informacin utiliza. Por ejemplo, la funcin + utiliza valores numricos, pero por
ejemplo, la funcin concat, que sirve para unir dos o ms cadenas para producir
otra ms larga, necesitas cadenas. Al evaluar la expresin:
(concat "abc" "def")
El valor producido ser abcdef.
Por ejemplo, si evaluamos lo siguiente:
(substring "The quick brown fox jumped." 16 19)
aparecer en el rea de eco la cadena fox. Hay que observar que la cadena que
se pasa a substring es un nico tomo aunque est formada por una cadena de
varias palabras separadas por espacios.
22
Al evaluar esta expresin hay que notar que hay espacios en blanco despus de la
palabra The y antes de la palabra red. La funcin number-to-string convierte
un nmero en una cadena. El resultado ser The 72 red foxes.
ARGUMENTOS
23
La funcin message
Como la funcin +, la funcin message toma un nmero variable de argumentos.
Se utiliza para enviar mensajes al usuario.
Un mensaje (message) es lo que aparece en el rea de eco. Por ejemplo, si
evaluamos lo sigiente:
(message "This message appears in the echo area!")
Aparecer en rea de eco la cadena entrecomillada completa.
(message "The name of this buffer is: %s." (buffer-name))
En este caso la funcin message toma dos argumentos una cadena y una expresin
que devuelve otra cadena: el nombre del buffer. Esa cadena se insertar en el
lugar donde la primera muestra el valor %s.
Para mostrar un valor como un entero, se utiliza %d igual que se utiliza %s. Por
ejemplo:
(message "The value of fill-column is %d." fill-column)
En este caso, el sistema mostrar al evaluar la lista The value of fill-column
is 70.
Tambin se pueden utilizar ms de un argumento en la funcin. Por ejemplo:
(message "There are %d %s in the office!"
(- fill-column 14) "pink elephants")
En la lnea de eco aparecer el mensaje There are 56 pink elephants in the
office!.
Igual que podemos calcular un nmero en lugar de pasrselo directamente,
tambin se puede sustituir la cadena por una expresin que al ser evaluada
produzca una cadena. Por ejemplo:
2 En la jerga de Lisp un predicado (predicate) es la referencia a una funcin para determinar
si una propiedad es verdadera o falsa. En este caso number-or-marker-p es el nombre de una
funcin que determina si el argumento que se ha pasado es un nmero o una marca.
24
Utilizando set
Para establecer el valor del smbolo flowers a la lista (rose violet daisy
buttercup) se evaluara la siguiente expresin:
(set 'flowers '(rose violet daisy buttercup))
Despus de evaluar la expresin set podemos evaluar la expresin flowers y
nos devolver la lista que hemos fijado.
flowers
Si evaluamos flowers, la variable con una comilla delante de ella, lo que veremos
en el rea de eco el mismo smbolo flowers. Para probarlo, evaluar:
'flowers
Hay que observar que al utilizar set se necesita poner la comilla ambos argumentos de la funcin. Cuando utilizamos set sin la comilla, se evala el argumento
antes de hacer ninguna otra cosa. Si haces esto, se obtiene un mensaje de error
del tipo Symbol's value as variable is void; y por otra parte, flowers no
devuelve un valor que pueda ser evaluado despus.
Utilizando setq
Como un uso prctico, al menos debes utilizar siempre la comilla del primer
argumento, el que indica la variable, en la funcin set. Por ello es comn utilizar
la forma especial setq. Esta funciona como set excepto que el primer argumento
ser quoted automticamente, por ello no es necesario hacerlo nosotros.
25
RESUMEN
Por ejemplo, para establecer el valor de la variable carnivores a una lista (lion
tiger leopard) utilizando setq podemos utilizar la siguiente expresin:
(setq carnivores '(lion tiger leopard))
Si hubiramos utilizado la funcin set, la expresin sera:
(set 'carnivores '(lion tiger leopard))
Tambin, setq permite asignar diferentes valores a diferentes variables. El primer
argumento se ata al segundo argumento, el tercero se ata al cuarto y as en
adelante. Por ejemplo:
(setq trees '(pine fir oak maple)
herbivores '(gazelle antelope zebra))
Contar
Un ejemplo que muestra cmo se puede utilizar setq como un contador. Al
evaluar las siguientes expresiones podremos verlo.
(setq counter 0)
(setq counter (+ counter 1))
counter
Resumen
Los programas Lisp estn compuestos de expresiones que son listas o
tomos.
Las listas estn compuestas de cero o ms tomos o listas internas separadas
por espacios en blanco encerradas entre parntesis. Una lista puede estar
vaca.
Los tomos son smbolos de varios caracteres, como forward-paragraph,
smbolos de un solo carcter +, cadenas de caracteres entre comillas o
nmeros.
Un nmero se evala como s mismo.
Una cadena entre comillas se evala tambin como s misma.
26
Ejercicios
Algunos ejercicios sencillos:
Genera un mensaje de error evaluando un smbolo correcto que no est
entre parntesis.
Genera un mensaje de error evaluando un smbolo correcto que est entre
parntesis.
Crea un contador que incremente por 2 en lugar de uno.
Escribe una expresin que muestre un mensaje en el rea de eco cuando se
evale.
Chapter 4
Practicar la evaluacin
Antes de meternos en definir funciones en Emacs Lisp, es mejor perder un poco
de tiempo en practicar la evaluacin de expresiones. Algunas funciones asociadas
con buffers son a la vez sencillas e interesantes.
Cmo evaluar
Cuando introduces un comando de edicin en Emacs Lisp, como puede ser mover
el cursor o deslizar la pantalla, ests evaluando una expresin.
Cuando tecleas algunas teclas haces que el intrprete de Lisp evale una expresin
y obtengas sus resultados. Incluso teclear texto plano implica evaluar una funcin
Emacs Lisp, en este caso una que usa self-insert-command, que nicamente
inserta el carcter que has pulsado.
Las funciones que evalas tecleando se llaman funciones interactivas o comandos.
Adems de teclear comandos por teclado, hay un segundo modo de evaluar una
expresin: colocar el cursor tras una lista y pulsar C-x C-e. Hay otras maneras
de evaluar una expresin, pero ya llegaremos a ellas.
En las siguientes secciones nos daremos cuenta de algunas funciones que nos
ayudarn a distinguir entre buffers y ficheros, como cambiar a un buffer y cmo
determinar la posicin en l.
27
28
Nombres de buffer
Las dos funciones buffer-name y buffer-file-name, muestra la diferencia entre
un fichero y un buffer. Cuando se evala la expresin (buffer-name) aparece
el nombre del buffer en el rea de eco. Si evaluamos (buffer-file-name)
aparecer el nombre del fichero. Cuando estamos editando un fichero la primera
devolver el nombre del fichero mientras que la segunda guarda el fichero con
todo el path.
(buffer-name)
(buffer-file-name)
Si utilizas C-u C-x C-e en lugar del habitual C-x C-e, el resultado aparece a
continuacin del cursor.
Obteniendo Buffers
La funcin buffer-name devuelve el nombre del buffer; para obtener el propio
buffer se necesita la funcin current-buffer. Al utilizar esta funcin en el
cdigo se obtiene el propio buffer.
(current-buffer)
Si evaluamos esa expresin de la manera habitual en Emacs, aparecer una
expresin como #<buffer nombre-buffer> en el rea de eco. Esto indica que
es el propio buffer lo que se ha obtenido.
Una funcin relacionada es other-buffer. Esto devuelve el buffer que se ha
utilizado recientemente.
(other-buffer)
Evaluando esto es posible que aparezca algo como #<buffer *scratch*> u otro
que se haya abierto el ltimo.
Cambiando de Buffer
La funcin switch-to-buffer cambia a otro buffer. Lo que hace la combinacin
de teclas C-x b es parecido:
(switch-to-buffer (other-buffer))
Si evaluamos esa expresin cambiaremos al ltimo buffer. En las siguientes
secciones se utilizar ms a menudo la funcin set-buffer. sta lo que hace es
cambiar el trabajo al buffer pero sin cambiarlo en pantalla. Cuando estamos
trabajando en un buffer y queremos ejecutar cdigo en otro, es mejor dejar la
ejecucin y los cambios fuera de la vista para no despistar al usuario.
29
Ejercicio
Carga un fichero con el que ests trabajando y muvete hacia el centro. Consigue
su nombre de buffer, nombre de fichero, tamao y posicin en el fichero.
30
Chapter 5
El macro defun
En Lisp, un smbolo como mark-whole-buffer tiene cdigo en l que le dice al
ordenador qu tiene que hacer cuando se llama a la funcin. Este cdigo se llama
definicin de la funcin y se crea evaluando una expresin Lisp que comienza
con el smbolo defun que es una abreviatura de define function.
Una definicin de funcin tiene cinco partes que siguen a la palabra defun:
1. El nombre del smbolo al que se debe atar la definicin de funcin.
31
32
Ayuda a pensar que las cinco partes de la definicin de funcin estn ordenadas
como una plantilla como:
(defun FUNCTION-NAME (ARGUMENTS...)
"OPTIONAL-DOCUMENTATION..."
(interactive ARGUMENT-PASSING-INFO)
BODY...)
; optional
Un ejemplo. Cdigo para una funcin que multiplica su argumento por 7. (Este
ejemplo no es interactivo.)
(defun multiply-by-seven (number)
"Multiplica NUMBER por siete."
(* 7 number))
Esta definicin comienza con un parntesis y el smbolo defun seguido por el
nombre de la funcin: multiply-by-seven.
La lista de argumentos contiene slo number. Se podra haber utilizado otra
palabra como por ejemplo multiplicando. Sin embargo, number nos dice qu
tipo de valor se espera. Se podra haber utilizado foogle, pero sera una
mala opcin porque no le dira nada a quien leyera el cdigo. La eleccin del
nombre depende del programador, que debera elegir nombres que clarifiquen el
significado de la funcin.
Por otro lado, se puede utilizar cualquier nombre para un smbolo en la lista
de argumentos, porque esa lista es privada para esa funcin particular. Para
entenderlo, de una forma llana veamos un ejemplo. Vamos a suponer que en tu
familia te han puesto un mote o sobrenombre: Chiki. Cuando en tu familia
alguien se refiere a Chiki est aludiendo de forma inequvoca a ti. Sin embargo,
es posible que en otro sitio, por ejemplo, en una pelcula hagan referencia a un
personaje con el nombre de Chiki. An habiendo dos entidades con el mismo
nombre, no se genera ningn problema. En tu familia t eres Chiki.
A la lista de argumentos le sigue la cadena de documentacin que describe la
funcin. Esta es lo que obtienes cuando tecleas C-h f y el nombre de la funcin.
Si escribes cadenas de documentacin de varias lneas debes tener en cuenta
que algunos comandos como apropos slo muestran la primera lnea. Tambin
hay que recordar que no hay que indentar la segunda linea de la cadena, porque
33
El efecto de la instalacin
Puedes ver el efecto de instalar multiply-by-seven evaluando el siguiente
ejemplo pulsando C-x C-e. El nmero 21 aparecer en el rea de eco.
(multiply-by-seven 3)
Si quieres puedes leer la documentacin de la funcin tecleando C-h f
(describe-function) y el nombre de la funcin multiply-by-seven.
Aparecer una ventana *Help* que dice:
multiply-by-seven is a Lisp function.
(multiply-by-seven NUMBER)
Multiplica NUMBER por siete.
34
multiply-by-seven interactiva
A continuacin se muestra tanto el uso especial de la forma interactive y una
manera de mostrar el valor en el rea de eco.
(defun multiply-by-seven (number)
; Interactive version.
"Multiply NUMBER by seven."
(interactive "p")
(message "The result is %d" (* 7 number)))
Al instalar el cdigo el nombre de la funcin aparecer en el rea de eco. Despus,
tecleando C-u y un nmero y despus M-x multiply-by-seven y pulsando
<RET>, aparecer el resultado en el rea de eco.
Hablando de forma general, puedes invocar una funcin como esta de dos
maneras:
35
36
let
La expresin let es una forma especial en Lisp que necesitar utilizar en muchas
funciones.
let se utiliza para atar un smbolo a un valor de modo que el intrprete Lisp
no confunda la variable con una variable con el mismo nombre que no sea parte
de la funcin.
Para comprender por qu la forma especial let es necesaria: cuando estamos
en nuestra casa nos referimos a la casa, como en hay que pintar la casa. Si
ests visitando un amigo y l dice la casa se estar refiriendo a su casa, no a
la tuya, es decir, a una casa diferente.
LET
37
Los valores fijados con let se descartan automticamente cuando acaba el let.
Por eso los efectos slo tienen efecto dentro de la expresin let.
let puede crear ms de una variable a la vez. Tambin proporciona un valor
inicial, tanto un valor especificado como nil. Despus de crear y asignar las
variables, let ejecuta el cdigo en el cuerpo y devuelve el valor de la ltima
expresin en el cuerpo.
38
La forma especial if
La tercera forma especial, adems de defun y let, es la condicional if. Esta
forma se utiliza para dejar que el ordenador tome decisiones.
La idea bsica de if es que si una prueba es cierta, entonces se evala una
expresin. Si la prueba no es cierta la expresin no se evala.
Ms detalles de if
Una expresin if escrita en Lisp no utiliza la palabra then; la prueba y la
accin son el segundo y el tercer elemento de una lista cuyo primer elemento es
if.
(if PRUEBA-CIERTO-O FALSO
EJECUTAR-ACCION-SI-ES-CIERTO)
Un ejemplo que se puede evaluar de manera habitual. La prueba es si el nmero
5 es mayor que el nmero 4 y como es verdadero mostrar el mensaje 5 es
mayor que 4!.
(if (> 5 4)
(message "5 is greater than 4!"))
; parte-if
; parte-then
LA FORMA ESPECIAL IF
39
40
Expresiones if-then-else
Una expresin if puede tener un tercer argumento llamado else-part, para el
caso de que la prueba devuelva false.
La palabra else no se escribe en el cdigo Lisp; la else-part de una expresin
if aparece tras la then-part.
(if TRUE-OR-FALSE-TEST
EJECUTAR-ACCION-SI-PRUEBA-DEVUELVE-TRUE
EJECUTAR-ACCION-SI-PRUEBA-DEVUELVE-FALSE)
Por ejemplo, la siguiente expresin muestra un mensaje 4 no es mayor que 5!
cuando se evala:
(if (> 4 5)
(message "4 falsely greater than 5!")
(message "4 is not greater than 5!"))
; if-part
; then-part
; else-part
SAVE-EXCURSION
41
Por ejemplo:
(if 4
'true
'false)
(if nil
'true
'false)
Por ejemplo, la expresin (> 5 4) devuelve t cuando se evala.
(> 5 4)
Por otro lado, esta funcin devuelve nil si la prueba es falsa.
(> 4 5)
save-excursion
La funcin save-excursion es la tercera y ltima forma especial que discutiremos
en este captulo.
En programas Emacs Lisp utilizados para edicin, la funcin save-excursion
es bastante habitual. Guarda la localizacin del punto, ejecuta el cuerpo de la
funcin y devuelve el punto a su posicin anterior si la localizacin ha cambiado.
Su propsito primario es que el usuario no se sorprenda ante un movimiento
inesperado del punto.
Punto y marca
Antes de comentar save-excursion vamos a ver lo que son el punto y la marca
en GNU Emacs. El Punto es la posicin actual del cursor. Donde est el
cursor ah est el Punto. En los terminales donde el cursor aparece como un
recuadro, el punto est inmediatamente antes de ese cuadro. En Emacs Lisp, el
punto es un entero. El primer carcter en el buffer es el uno, el segundo es el
dos y as en adelante. La funcin point devuelve la posicin actual del cursor.
Cada buffer tiene su correspondiente valor de punto.
La marca es otra posicin en el buffer; su valor se fija con el comando C-<SPC>
(set-mark-command). Si se ha fijado la marca, se puede utilizar el comando C-x
C-x (exchange-point-and-mark) para hacer que el cursor salte a la marca y
ponga la marca en la posicin previa del punto. Adems, si se fija otra marca, la
posicin de la marca previa se guarda en el anillo de marcas. Se puede marcar
posiciones de marcas de esta manera. Puedes hacer saltar el cursor a una marca
guardada tecleando C-u C-<SPC> una o ms veces.
42
Resumen
eval-last-sexp Evala la ltima expresin simblica anterior a la posicin del
punto. El valor se muestra en el rea de eco a no ser que se invoque la
funcin con un argumento; en ese caso, la salida se imprime directamente
en el buffer. El comando normalmente est atado a C-x C-e.
RESUMEN
43
defun Definir funcin. Esta macro tiene cinco partes: el nombre, una plantilla
para los argumentos que se pasarn a la funcin, documentacin, una
declaracin interactiva opcional y el cuerpo de la definicin.
Por ejemplo, en Emacs la definicin de la funcin dired-unmark-all-marck
es como sigue.
(defun dired-unmark-all-marks ()
"Remove all marks from all files in the Dired buffer."
(interactive)
(dired-unmark-all-files ?\r))
interactive Declara al intrprete que la funcin se puede usar interactivamente.
Esta forma especial puede estar seguida de una cadena con una ms partes
que pasan informacin a los argumentos de la funcin en secuencia. Estas
partes pueden tambin decirle al intrprete que pregunte por informacin.
Las partes de la cadena se deben separar con saltos de lnea \n.
Caracteres de cdigo comunes:
b El nombre de un buffer existente.
f El nombre de un fichero existente.
p El argumento prefijo numrico. (Hay que observar que esta p es minscula.)
r El punto y la marca como dos argumentos numricos, el ms pequeo
primero. Esta es la nica letra cdigo que especifica dos argumentos
sucesivos en lugar de uno.
let Declara que una lista de variables es para usarse en el cuerpo de let y les da
un valor inicial, bien nil u otro valor; luego evala el resto de expresiones
en el cuerpo de let y devuelve el ltimo. Dentro del cuerpo de let, el
intrprete de Lisp no mira los valores de las variables con el mismo nombre
que se encuentren fuera del let.
Por ejemplo:
(let ((foo (buffer-name))
(bar (buffer-size)))
(message
"This buffer es %s and has %d characters."
foo bar))
save-excursion Guarda el valor del punto y el buffer actual antes de evaluar
el cuerpo de esta forma especial. Restaura el valor del punto y el buffer
despus.
Por ejemplo:
(message "We are %d characters into this buffer."
(- (point)
44
EJERCICION
45
Ejercicion
Escribe una funcin no interactiva que duplique el valor de su argumento,
un nmero. Haz esta funcin interactiva.
46
Chapter 6
Buscando ms
En este paseo se describirn las funciones a veces en detalle y a veces brevemente.
Si te interesa obtener una documentacin completa de cualquier funcin de
Emacs Lisp lo puedes hacer tecleando C-h f y luego el nombre de la funcin.
Tambin, describe-function te dir el lugar de la definicin de la funcin.
De manera ms general, si quieres ver una funcin en su fichero fuente original,
puedes utilizar la funcin xref-find-definitions para saltar a ella.
Para utilizar el comando xref-find-definitions, teclee M-. (o pulsa <ESC>
y luego el punto) y luego en el prompt teclea el nombre de la funcin de la que
quieres ver el cdigo fuente, como por ejemplo mark-whole-buffer y despus
pulsa <RET>.
Los ficheros que contienen cdigo Lisp son convencionalmente llamados libreras. En el Manual de GNU Emacs se puede encontrar que el comando C-h p
te permite buscar en las libreras estndar de Emacs Lisp por los nombres clave.
47
La expresin interactiva le dice a Emacs que la funcin se puede utilizar interactivamente. En este ejemplo, interactive no tiene argumentos porque
simplified-beginning-of-buffer no requiere ninguno.
El cuerpo de la funcin consiste en dos lneas:
(push-mark)
(goto-char (point-min))
La primera de estas lneas es la expresin (push-mark). Cuando se evala,
establece la marca en la posicin actual del cursor. La posicin de esta marca se
guarda en el anillo de marcas.
La siguiente lnea es (goto-char (point-min)). Esta expresin hace saltar el
cursor al punto mnimo del buffer, es decir, al inicio del mismo.
Cuando ests leyendo cdigo y llegas a una funcin no familiar, como goto-char,
puedes encontrar el comando describe-function. Para usar este comando
teclea C-h f y despus el nombre de la funcin. Por ejemplo, la documentacin
para goto-char es:
LA DEFINICIN DE MARK-WHOLE-BUFFER
49
La definicin de mark-whole-buffer
La funcin mark-whole-buffer no es ms complicada de entender que la funcin
simplified-beginning-of-buffer. En este caso, sin embargo, veremos la
funcin completa, no la versin simplificada.
Un vistazo a mark-whole-buffer
En GNU Emacs 22, el cdigo completo de lafuncin es como sigue:
(defun mark-whole-buffer ()
"Put point at beginning and mark at end of buffer.
You probably should not use this function to use any subroutine
that uses or sets the mark."
(interactive)
(push-mark (point))
(push-mark (point-max) nil t)
(goto-char (point-min)))
Al nombre de la funcin (mark-whole-buffer) lo sigue una lista de argumentos
vaca (), que significa que la funcin no necesita argumentos. A continuacin va
la cadena de documentacin. La siguiente lnea indica a Emacs que se puede
utilizar interactivamente y luego est el cuerpo, que lo veremos en el siguiente
punto.
El cuerpo de mark-whole-buffer
El cuerpo de la funcin mark-whole-buffer consiste en tres lneas de cdigo:
(push-mark (point))
(push-mark (point-max) nil t)
(goto-char (point-min))
La primera lnea es la expresin (push-mark (point)), que como ya vimos
antes hace que el intrprete de Lisp fije una marca en la posicin actual del
cursor. Aunque en la anterior ocasin apareca push-mark sin argumento. Quiz
sea porque en esta ocasin el que escribi la funcin pens que el argumento no
era opcional o porque quera guardar la misma estructura que las otras lneas.
La definicin de append-to-buffer
El comando append-to-buffer es ms complejo que el anterior. Lo que hace
es copiar la regin desde el buffer actual a un buffer especificado.
Un vistazo a append-to-buffer
El comando append-to-buffer utiliza la insert-buffer-substring que copia
la regin y la inserta en otro buffer. La mayor parte de append-to-buffer se
dedica a establecer las condiciones para que trabaje insert-buffer-substring.
(defun append-to-buffer (buffer start end)
"Append to specified buffer the text of the region.
It is inserted into that buffer before its point.
When calling from a program, give three arguments:
BUFFER (or buffer name), START and END.
START and END specify the portion of the current buffer to be copied."
(interactive
(list (read-buffer "Append to buffer: " (other-buffer
(current-buffer) t))
(region-beginning) (region-end)))
(let ((oldbuf (current-buffer)))
(save-excursion
(let* ((append-to (get-buffer-create buffer))
(windows (get-buffer-window-list append-to t t))
point)
(set-buffer append-to)
(setq point (point))
LA DEFINICIN DE APPEND-TO-BUFFER
51
(barf-if-buffer-read-only)
(insert-buffer-substring oldbuf start end)
(dolist (window windows)
(when (= (window-point window) point)
(set-window-point window (point))))))))
La primera lnea de la funcin incluye su nombre y tres argumentos. Los
argumentos son el buffer en el que se copiar el texto y en inicio (start) y el final
(end) de la regin que se copiar del buffer actual.
La siguiente parte de la funcin es la documentacin que es clara y completa.
Es una convencin escribir los nombres de los argumentos en maysculas para
verlos rpidamente. Adems siempre es mejor citarlos en el mismo orden que
aparecen en la lista de argumentos.
Hay que hacer notar que la documentacin distingue entre un buffer y su nombre.
El cuerpo de append-to-buffer
El cuerpo de append-to-buffer comienza con let.
La plantilla de
append-of-buffer con la expresin let hace que la funcin quede as:
(defun append-to-buffer (buffer start end)
"DOCUMENTATION..."
(interactive...)
(let ((VARIABLE VALOR))
CUERPO...))
La expresin let tiene tres elementos:
1. El smbolo let.
2. Una lista de variables conteniendo en este caso una simple lista de dos
valores (VARIABLE VALOR).
3. El cuerpo de la expresin let.
En la funcin append-to-buffer la lista de variables aparece como:
(oldbuf (current-buffer))
En esta parte, la nica variable oldbuf se ata al valor devuelto por la expresin
(current-buffer). La variable oldbuf se utiliza para manejar el buffer en el
que ests trabajando y desde el que se copiar.
El elemento o elementos de la lista de variables se encerrarn entre parntesis
para que el intrprete de Lisp pueda distinguir entre la lista de variables y el
cuerpo de let. La lnea se ver as:
(let ((oldbuf (current-buffer)))
... )
save-excursion en append-to-buffer
El cuerpo de la expresin let en append-to-buffer consiste en una expresin
save-excursion.
La funcin save-excursion guarda la localizacin del punto y lo restaura
despus de que el cuerpo de save-excursion se ejecute por completo. Adems
LA DEFINICIN DE APPEND-TO-BUFFER
53
Resumen
describe-function
describe-variable Muestra la documentacin de una funcin o variable. Convencionalmente atados a C-h f y C-h v.
find-tag Busca el fichero que contiene el cdigo fuente de una funcin o variable
y cambia los buffers a l, posicionando el punto al comiendo del tem.
Convencionalmente M-..
save-excursion Guarda la posicin del punto y restaura su valor despus de
que se hayan evaluado sus argumentos. Tambin recuerda el buffer actual
y regresa a l.
push-mark Pone marca en una posicin y graba el valor de la marca previa en
el anillo de marcas. La marca es una posicin en el buffer que mantendr
su posicin relativa incluso si se aade o borra texto del buffer.
goto-char Pone el punto en la posicin especificada por el valor del argumento,
que debe ser un nmero, una marca, o una expresin que devuelva el
nmero de una posicin, como puede ser point-min.
insert-buffer-substring Copia una regin de texto desde un buffer que se
pasa a la funcin como argumento e inserta la regin en el buffer actual.
EJERCICIOS
55
Ejercicios
Escribe tu propia definicin de funcin simplified-end-of-buffer y
luego prueba si funciona.
Utiliza if y get-buffer para escribir una funcin que muestre un mensaje
dicindote si existe un buffer.
Utiliza find-tag, busca el cdigo fuente para la funcin copy-to-buffer.
Chapter 7
Algunas funciones ms
complejas
En este captulo aprenderemos mirando en algunas funciones complejas. La
funcin copy-to-buffer ilustra el uso de dos expresiones save-excursion en
una definicin mientras la funcin insert-buffer ilustra el uso de un asterisco
en una expresin interactive, uso de or y la importante diferencia entre un
nombre y el objeto al que se refiere el nombre.
La definicin de copy-to-buffer
Despus de comprender cmo trabaja append-to-buffer es fcil comprender
copy-to-buffer. Esta funcin copia texto en un buffer pero en lugar de aadirlo
al segundo buffer, reemplaza todo el texto anterior del segundo buffer.
El cuerpo de copy-to-buffer aparece sigue,
...
(interactive "BCopy to buffer: \nr")
(let ((oldbuf (current-buffer)))
(with-current-buffer (get-buffer-create buffer)
(barf-if-buffer-read-only)
(erase-buffer)
(save-excursion
(insert-buffer-substring oldbuf start end)))))
La funcin copy-to-buffer tiene una expresin interactive ms sencilla que
append-to-buffer.
La definicin entonces dice
57
58
Argumentos opcionales
Lisp espera que una funcin con un argumento en la definicin se llame con un
valor para ese argumento. Si eso no ocurre se obtendr un error que dice Wrong
number of arguments.
59
Sin embargo, una de las propiedades de Lisp son los argumentos opcionales: se
utiliza una palabra clave para indicarle al intrprete de Lisp que un argumento
es opcional. Esa palabra clave es &opcional. El & delante es parte de la palabra
clave. En la definicin de una funcin si el argumento sigue a la palabra clave
&opcional no se necesita pasar un valor cuando se llama a esa funcin.
La primera lnea de la funcin sera:
(defun beginning-of-buffer (&optional arg)
Un esquema de la funcin completa sera como:
(defun beginning-of-buffer (&optional arg)
"DOCUMENTACION..."
(interactive "P")
(or (EL-ARGUMENTO-ES-UNA-CONS-CELL arg)
(and ESTAN-ACTIVOS-TRANSIENT-MARK-MODE-Y-MARK-ACTIVE-TRUE)
(push-mark))
(let (DETERMINAR-TAMAO-Y-FIJARLO)
(goto-char
(SI-HAY-UN-ARGUMENTO
CALCULAR-DONDE-IR
ELSE-IR
(point-min))))
DO-NICETY
La funcin es similar a simplified-beginning-of-buffer excepto que la expresin interactive tiene un argumento P y la funcin goto-char est seguida
por una expresin if-then-else que calcula donde poner el cursor si hay un
argumento que no es una cons cell.
La P en la expresin interactive le dice a Emacs que pase un argumento, si
hay uno, a la funcin.
La prueba de verdadero o falso en la expresin if parece compleja, pero no lo
es: comprueba cuando arg tiene un valor que no sea nil ni una cons cell. Si
no hay argumento el punto sencillamente ser point-min que se pasar y la
expresin quedara como (goto-char (point-min)) que es como aparece en su
modo simplificado.
60
(/
(+ 10
(*
size (prefix-numeric-value arg))) 10)))
Desentraar beginning-of-buffer
(if (BUFFER-ES-GRANDE
DIVIDIR-TAMAO-BUFFER-POR-10-Y-MULTIPLICAR-POR-ARG
DE-OTRO-MODO-UTILIZAR-CALCULO-ALTERNATIVO
La razn para esto es que las versiones de Emacs anteriores a las 18 utiliza
nmeros no superiores a los ocho millones. Por esto el programador previniendo
el overflow.
Hay dos casos: el buffer es grande o no lo es.
Qu ocurre con un buffer grande
En beginning-of-buffer, el interior de la expresin if comprueba si el tamao
del buffer es mayor que 10.000 caracteres. Para hacer esto, utiliza la funcin > y
el clculo de size.
(if (> size 10000)
Cuando el buffer es grande la parte de la expresin que se evala es
(*
(prefix-numeric-value arg)
(/ size 10))
61
beginning-of-buffer completa
Aqu la funcin completa:
(defun beginning-of-buffer (&optional arg)
"Move point to the beginning of the buffer;
leave mark at previous position.
With \\[universal-argument] prefix,
do not set mark at previous position.
With numeric arg N,
put point N/10 of the way from the beginning.
If the buffer is narrowed,
this command uses the beginning and size
of the accessible part of the buffer.
Don't use this command in Lisp programs!
\(goto-char (point-min)) is faster
and avoids clobbering the mark."
(interactive "P")
(or (consp arg)
(and transient-mark-mode mark-active)
(push-mark))
(let ((size (- (point-max) (point-min))))
(goto-char (if (and arg (not (consp arg)))
(+ (point-min)
(if (> size 10000)
62
Excepto por dos pequeos detalles, ya hemos visto cmo funciona la funcin.
El primer detalle es la cadena rara en la documentacin:
\\[universal-argument]
Se utiliza \\ antes del primer corchete. Le dice al intrprete de Lisp la clave
que est entre los corchetes [...]. En este caso universal-argument que
normalmente es C-u pero puede ser diferente.
Finalmente, la ltima lnea que dice que mueva el punto al comienzo la siguiente
lnea si el comando se invoca con un argumento:
(if (and arg (not (consp arg))) (forward-line 1))
La parte (not (consp arg)) es para cuando se especifica el comando con C-u,
pero sin un nmero, el comando no colocar el cursor al comienzo de la segunda
lnea.
Resumen
or Evala cada argumento secuencialmente y devuelve el valor del primer
argumento que no sea nil. Si ningn valor es distinto de nil, devolver
nil. En resumen, devuelve un valor true si uno o ms elementos son
true.
and Evala cada argumento secuencialmente y si cualquiera es nil devolver
nil; si no hay ninguno nil devolver el valor del ltimo argumento. En
resumen, devolver un valor true slo si todos los argumentos son true.
&optional Una palabra clave que indica que un argumento en una funcin es
opcional; esto significa que la funcin se puede evaluar sin el argumento, si
se quiere.
prefix-numeric-value Convierte un argumento prefijo crudo producido por
(interactive "P") en un valor numrico.
forward-line Mueve el punto hacia adelante al principio de la lnea siguiente.
erase-buffer Borra todo el contenido del buffer actual.
bufferp Devuelve t si su argumento es un buffer; si no devolver nil.
63
64
Chapter 8
Narrowing y Widening
Narrowing (estrechamiento) es una caracterstica de Emacs que hace posible que
centrarte en una parte especfica de un buffer y trabajar sin cambiar accidentalmente otras partes. Normalmente est desactivada porque puede ser confuso
para novatos.
66
(save-restriction
BODY... )
El cuerpo de save-restriction es una o ms expresiones que el intrprete Lisp
evaluar secuencialmente.
Cuando utilizas save-excursion y save-restriction, una despus de la otra,
deberas utilizar save-excursion en el exterior. Si las escribes en el orden
inverso, puede fallar el registro del narrowing. La plantilla para utilizar las dos
sera:
(save-excursion
(save-restriction
BODY...))
En otras circunstancias, cuando no las escribes juntas, se pueden escribir en el
orden apropiado a la funcin. Por ejemplo:
(save-restriction
(widen)
(save-excursion
BODY...))
what-line
El comando what-line te dice el nmero de la lnea en la que se sita el cursor.
La funcin ilustra el uso de los comandos save-restriction y save-excrusion.
El cdigo original de la funcin:
(defun what-line ()
"Print the current line number (in the buffer) of point."
(interactive)
(save-restriction
(widen)
(save-excursion
(beginning-of-line)
(message "Line %d"
(1+ (count-lines 1 (point)))))))
En versiones recientes de GNU Emacs la funcin se ha expandido un poco y es
ligeramente distinta.
La funcin what-line muestra una lnea de documentacin y es interactiva. Las
siguientes dos lneas utilizan las funciones save-restriction y widen.
La forma especial save-restriction anota si est activo el narrowing en el
buffer y restaura ese estrechamiento despus de que se evale el cdigo en el
cuerpo de save-restriction.
67
68
Chapter 9
Funciones fundamentales
car, cdr, cons
En Lisp, las funciones car, cdr y cons son fundamentales. La funcin cons se
utiliza para construir listas y las funciones car y cdr se utilizan para tomar
partes de ellas.
Nombres raros
El nombre de cons es una abreviatura de construct (construccin). El origen
de los nombres car y cdr es ms esotrico: car es un acrnimo de la frase
/Contents of the Address part of the Register/ y cdr de la frase /Contents
of the Decrement part of the Register/. Estas frases se refieren a partes del
hardware de los inicios de la computacin de cuando se desarroll el Lisp original.
Aunque estn obsoletas, las frases se mantienen en uso.
car y cdr
El CAR de una lista es muy simple, el primer tem de la lista. As el CAR de la
lista (rose violet daisy buttercup) es rose.
Claramente, un nombre ms razonable para la funcin car sera first como se
ha sugerido frecuentemente.
car no quita el primer elemento de la lista, slo informa cul es. Despus de
que se aplique car a una lista, la lista contina siendo la misma. En la jerga,
car es no destructiva. Esta caracterstica ser importante.
69
70
El CDR de una lista es el resto de la lista despus del primer elemento. Esto
quiere decir, con el mismo ejemplo anterior que si el CAR de la lista (rose
violet daisy buttercup) era rose el CDR es (violet daisy buttercup).
Claramente, un nombre ms razonable para cdr sera rest.
Cuando car y cdr se aplican a la lista de smbolos, como la lista (pine fir
oak maple), el elemento de la lista devuelto por la funcin car es el smbolo
pine sin ningn parntesis. Sin embargo, el CDR de la lista es una lista tambin
(fir oak maple).
Por otro lado, en una lista de listas, el primer elemento es una lista. car devolver
el primer elemento como una lista. Por ejemplo:
(car '((lion tiger cheetah)
(gazelle antelope zebra)
(whale dolphin seal)))
Puesto que car y cdr no son destructivas, no modifican las listas a las que se
aplican. Esto es muy importante para cmo se usan.
Las funciones car y cdr se utilizan para separar listas y se consideran fundamentales en Lisp. Aunque no pueden separar o tener acceso a las partes de un
array; un array se considera un tomo. La otra funcin cons puede construir
una lista, pero no un array.
cons
La funcin cons construye listas, sera la inversa de car y cdr. Por ejemplo,
cons puede utilizarse para hacer una lista de cuatro elementos desde una lista
de tres elementos (fir oak maple):
(cons 'pine '(fir oak maple))
Como car y cdr, cons es no destructiva.
NTHCDR
71
nthcdr
La funcin nthcdr est asociada con la funcin cdr. Lo que hace es tomar el
CDR de una lista repetidamente.
72
Si tomas el CDR de la lista (pine fir oak maple), obtendrs la lista (fir
oak maple). Si lo repites otra vez obtienes (oak maple). Si continas llegars
a obtener () o dicho de otro modo nil.
Por ejemplo:
(cdr (cdr '(pine fir oak maple)))
(oak maple)
La funcin nthcdr hace lo mismo repitiendo la llamada a cdr. En el siguiente
ejemplo, se pasa el argumento 2 antes de la lista y el valor devuelto es la listas
sin los dos primeros tems, que es lo mismo que repetir dos veces cdr en la lista:
(nthcdr 2 '(pine fir oak maple))
(oak maple)
Utilizando la lista de cuatros elementos original podemos ver los siguientes
resultados para entender cmo funciona.
;; Dejar la lista como es
(nthcdr 0 '(pine fir oak maple))
(pine fir oak maple)
;; Devolver una copia sin el primer elemento
(nthcdr 1 '(pine fir oak maple))
(fir oak maple)
;; Devolver una copia sin los primeros tres elementos
(nthcdr 3 '(pine fir oak maple))
(maple)
;; Devolver una lista sin los cuatro elementos
(nthcdr 4 '(pine fir oak maple))
nil
;; Devolver una lista sin ningn elemento
(nthcdr 5 '(pine fir oak maple))
nil
nth
La funcin nth devuelve un determinado elemento de la lista.
Esta funcin est definida en C por cuestin de velocidad, pero si no lo estuviera,
su cdigo sera algo as:
(defun nth (n list)
"Returns the Nts element of LIST.
SETCAR
73
setcar
Tal como se puede sospechar por los nombres, setcar y setcdr son funciones
que establecen el CAR y el CDR de una lista a un nuevo valor. Al contrario que
car y cdr s modifican la lista original.
Vamos a verlo con un ejemplo que se pueda evaluar en Emacs.
(setq animals '(antelope giraffe lion tiger))
Si evaluamos la lnea anterior el intrprete atar la lista pasada a la variable
animals. As si evaluamos
animals
(antelope giraffe lion tiger)
A continuacin utilizaremos la funcin setcar con dos argumentos, la variable
animals y el smbolo hippopotamus con apstrofe.
(setcar animals 'hippopotamus)
Si evaluamos ahora la variable animals obtendremos (hippopotamus giraffe
lion tiger). La funcin setcar ha sustituido antelope con hippopotamus:
ha cambiado la lista.
setcdr
La funcin setcdr es similar a la funcin setcar excepto que la funcin reemplaza
desde el segundo al ltimo elemento. Por ejemplo:
74
Ejercicio
Construye una lista de cuatro pjaros, evaluando algunas expresiones con cons.
Mira a ver qu ocurre cuando pasas cons a una lista consigo misma. Reemplaza
el primer elemento de la lista de cuatro pjaros con un pez. Reemplaza el resto
de esa lista con otro pez.
Chapter 10
76
llama a una funcin que invoca una funcin que manipula el kill ring.
zap-to-char
Vamos a echar un vista a la funcin interactiva zap-to-char.
La expresin interactive
La expresin interactive en el comando zap-to-char aparece como:
(interactive "p\ncZap to char: ")
La parte entre comillas especificar dos cosas diferentes. Primero, y ms simple,
es la p. Esta parte separada de la siguiente por una nueva lnea \n. La p significa
que el primer argumento a la funcin pasar un valor a processed prefix. El
argumento prefijo se pasar tecleando C-u y un nmero o M- y un nmero. Si la
funcin se llama interactivamente sin un prefijo, se pasar uno en ese argumento.
ZAP-TO-CHAR
77
El cuerpo de zap-to-char
El cuerpo de la funcin zap-to-char contiene cdigo que mata (esto es, borra)
el texto en la regin entre la posicin del cursor y el caracter especificado.
La primera parte del cdigo sera:
(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)))
char-table-p es una funcin que no hemos utilizado hasta ahora. Determina si
su argumento es un carcter de la tabla.
(point) es la posicin actual del cursor.
La siguiente parte del cdigo es una expresin utilizando progn. El cuerpo de
progn consiste en llamar a search-forward y point. Es ms fcil comprender
cmo funciona progn despus de aprender algo sobre search-forward, por eso
veremos sta ltima primero.
La funcin search-forward
La funcin search-forward se utiliza para localizar el carcter hasta el que
borrar en zap-to-char. Si la bsqueda tiene xito, search-forward deja el
punto inmediatamente despus del en la cadena objetivo.
En zap-to-char, la funcin search-forward aparece as:
(search-forward (char-to-string char) nil nil arg)
La funcin search-forward toma cuatro argumentos:
1. El primer argumento es el objetivo que se buscar.
Ocurre que el argumento pasado a zap-to-char es un slo carcter. Dentro
del ordenador, un carcter tiene una estructura diferente a una cadena de
un slo carcter. Sin embargo, la funcin espera una cadena, esa conversin
la hace char-to-string.
78
KILL-REGION
79
Resumiendo zap-to-char
Ahora que ya est claro cmo funcionan search-forward y progn podemos
comprender cmo funciona zap-to-char.
El primer argumento a kill-region es la posicin del cursor cuando se llama
la funcin zap-to-char. Despus se realiza la bsqueda y el valor del punto
cambia. La nueva posicin se pasa como segundo argumento y la funcin borrar
la regin.
La forma especial progn es necesaria porque el comando kill-region toma dos
argumentos y lo que hace es agrupar en uno slo, la bsqueda de search-forward
y la expresin del resultado point, que se pasar como segundo argumento.
kill-region
Esta funcin corta texto de una regin y copia ese texto en el kill ring, desde
el que se puede recuperar.
En esencia, la funcin kill-region llama a condition-case que toma tres
argumentos. En esta funcin el primer argumento no hace nada. El segundo
argumento contiene el cdigo que hace el trabajo cuando todo va bien. El tercero
contienen el cdigo que se llama si ocurre un error.
80
;;
;;
;;
;;
'when' is an 'if' without an else-part. The second 'when'
;;
again checks whether the current string exists; in
;;
addition, it checks whether the previous command was
;;
another call to 'kill-region'. If one or the other
;;
condition is true, then it sets the current command to
;;
be 'kill-region'.
(let ((string (filter-buffer-substring beg end t)))
(when string
;STRING is nil if BEG = END
;; Add that string to the kill ring, one way or another.
(if (eq last-command 'kill-region)
;; - 'yank-handler' is an optional argument to
;;
'kill-region' that tells the 'kill-append' and
;;
'kill-new' functions how deal with properties
;;
added to the text, such as 'bold' or 'italics'.
(kill-append string (< end beg) yank-handler)
(kill-new string nil yank-handler)))
(when (or string (eq last-command 'kill-region))
(setq this-command 'kill-region))
nil)
;; * The third argument to 'condition-case' tells the interpreter
;;
what to do with an error.
;;
The third argument has a conditions part and a body part.
;;
If the conditions are met (in this case,
;;
if text or buffer are read-only)
;;
then the body is executed.
;;
The first part of the third argument is the following:
((buffer-read-only text-read-only) ;; the if-part
;; ... the then-part
(copy-region-as-kill beg end)
KILL-REGION
81
;;
Next, also as part of the then-part, set this-command, so
;;
it will be set in an error
(setq this-command 'kill-region)
;;
Finally, in the then-part, send a message if you may copy
;;
the text to the kill ring without signaling an error, but
;;
don't if you may not.
(if kill-read-only-ok
(progn (message "Read only text copied to 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)))))
condition-case
Normalmente cuando Emacs Lisp falla evaluando una expresin para el programa
y muestra un mensaje. Sin embargo, algunos programas cuando emprenden
acciones complicadas, no pueden simplemente devolver un error.
Con la funcin kill-region puede ocurrir que se intente borrar texto en un
buffer de solo lectura y no se pueda. Por eso kill-region tiene cdigo que
maneja esa circunstancia. Ese cdigo, se encuentra dentro de la forma especial
condition-case.
La plantilla de condition-cases es:
(condition-case
VAR
BODYFORM
ERROR-HANDLER...)
El segundo argumento, BODYFORM, es claro.
La forma especial
condition-case hace que el intrprete de Lisp evale el cdigo de BODYFORM.
Dicho de otro modo, la parte BODYFORM de una expresin condition-case
determina qu ocurrir cuando todo funciona correctamente.
Sin embargo, si ocurre un error, la funcin genera una seal de error que definir
una o ms nombres de condiciones de error.
El tercer argumento es un manejador de errores. Un manejador de errores tiene
dos partes un NOMBRE-CONDICION y un CUERPO.
Lisp macro
La parte del cdigo de la expresin condition-case que se evala cuando todo
va bien tiene un when. El cdigo utiliza when para determinar si la variable
string apunta a texto que existe.
82
copy-region-as-kill
La funcin copy-region-as-kill copia una regin de texto de un buffer y la
guarda en el kill-ring.
Si llamas a copy-region-as-kill inmediatamente tras un comando
kill-region, Emacs aade el texto copiado al anterior.
COPY-REGION-AS-KILL
83
El cuerpo de copy-region-as-kill
La funcin copy-region-as-kill funciona del mismo modo que lo hace la
funcin kill-region. Ambas estn escritas de modo que dos o ms matados se
combinan en una sola entrada. Si tiras del texto desde el anillo de matados, se
obtiene todo en una pieza.
last-command y this-command
Normalmente, cuando se ejecuta una funcin, Emacs establece el valor
this-command a la funcin que se est ejecutando (que en este caso ser
84
La funcin kill-append
La funcin kill-append sera como sigue:
(defun kill-append (string before-p &optional yank-handler)
"Append STRING to the end of the latest kill in the kill ring.
If BEFORE-P is non-nil, prepend STRING to the kill.
..."
(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 'yand-handler cur)))
yand-handler)))
Utiliza la funcin let* para establecer el valor del primer elemento del anillo de
matados el cur.
Hay que considerar el primer argumento a kill-new es un condicional. Utiliza
concat para concatenar el texto nuevo al CAR del anillo de matados aadindolo
antes o despus dependiendo del valor de before-p.
(if before-p
(concat string cur)
(concat cur string))
; if-part
; then-part
; else-part
COPY-REGION-AS-KILL
85
end beg), y resulta true el texto se colocar delante del texto anterior. Segn
su valor se evaluar (concat string cur) o (concat cur string).
Para comprender como trabaja vamos a ver algunos ejemplos:
(concat "abc" "def")
"abcdef"
(concat "new "
(car '("first element" "second element")))
"new first element"
(concat (car
'("first element" "second element")) " modified")
"first element modified"
La funcin kill-new
La funcin kill-new funciona como sigue:
(defun kill-new (string &optional replace yank-handler)
"Make STRING the latest kill in the kill ring.
Set `kill-ring-yank-pointer' to point to it.
If `interprogram-cut-function' is non-nil, apply it to STRING.
Opcional second argument REPLACE non-nil means that STRING will replace
the front of the kill ring, rather than being added to the list.
..."
(if (> (length string) 0)
(if yank-handler
(put-text-property 0 (length string)
'yank-handler yank-handler string))
(if yank-handler
(signal 'args-out-of-range
(list string "yank-handler specified for empty string"))))
(if (fboundp 'menu-bar-update-yank-menu)
(menu-bar-update-yank-menu string (and replace (car kill-ring))))
(if (and replace kill-ring)
(setcar kill-ring string)
(push string kill-ring)
(if (> (length kill-ring) (kill-ring-max)
(setcdr (nthcdr (1- kill-ring-max) kill-ring) nil)))
(setq kill-ring-yank-pointer kill-ring)
(if interprogram-cut-function
(funcall interprogram-cut-function string (not replace)))))
Hay que observar que la funcin no es interactiva.
86
Digresin en C
La funcin copy-region-as-kill utiliza la funcin filter-buffer-substring,
que a su vez utiliza la funcin delete-and-extract-region.
Al contrario que otro cdigo que se discute aqu, la ltima funcin no est escrita
en Emacs Lisp. Est escrita en C y es una de las primitivas del sistema de GNU
Emacs.
Como muchas de otras primitivas de Emacs, delete-and-extract-region est
escrita como una instancia de un macro C. El macro completo sera as:
DEFUN ("delete-and-extract-region", Fdelete_and_extract_region,
Sdelete_and_extract_region, 2, 2, 0,
doc: /* Delete the text between START and END and return it.
{
*/ )
DIGRESIN EN C
87
Sin entrar en los detalles del proceso de escribir macros, vamos a explicarlo un
poco. Lo primero es que el macro comienza con la palabra DEFUN. Esta palabra
se utiliza con el mismo propsito que defun en Lisp.
La palabra DEFUN est seguida de siete partes dentro de parntesis:
La primera parte es el nombre de la funcin en Lisp, delete-and-extract-region.
La segunda parte es el nombre de la funcin en C, Fdelete_and_extract_region.
Por convencin, comienza con F. Puesto que C no utiliza guiones en los
nombres, se utilizan subrayados en su lugar.
La tercera parte es el nombre de la estructura constante en C que guarda
informacin en esta funcin para uso interno. Es el mismo nombre de la
funcin en C pero comienza con S en lugar de F.
La cuarta y quinta especifican el mnimo y mximo nmero de argumentos que puede tener la funcin. Esta funcin requiere exactamente 2
argumentos.
La sexta parte es como el argumento que sigue a la declaracin interactive
en una funcin escrita en Lisp: una letra seguida, quiz, por un prompt.
La nica diferencia con Lisp es cuando el macro se llama sin argumentos.
Entonces se escribe un 0, como en este macro.
Si se especifican argumentos, hay que ponerlos entre comillas.
La sptima parte es una cadena de documentacin como una escrita en
Emacs Lisp. Esta est escrita como un comentario de C.
En un macro C, los parmetros formales vienen a continuacin, con una
declaracin de qu tipo de objeto son, seguidos por el cuerpo del macro. Para
delete-and-extract-region el cuerpo consiste en las siguientes cuatro lneas:
validate_region (&start, &end);
if (XINT (start) == XINT (end))
return empty_unibyte_string;
return del_range_1 (XINT (start), XINT (end), 1, 1)
La funcin validate_region comprueba si los valores pasados como principio
y final de la regin son tipos apropiados y estn en rango. Si las posiciones de
principio y final son las misma, devuelve una cadena vaca.
La funcin del_range_1 borra el texto. Es una funcin compleja y no miraremos
dentro. Actualiza el buffer y hace otras cosas.
88
Para lo concerniente al lenguaje C, start y end son dos enteros que marcan el
comiendo y el final de la regin borrada.1
El tamao del entero depende de la mquina (32 64 bits).
XINT es una macro C que extrae el nmero relevante de una larga coleccin de
bits.
Desde el punto de vista de una persona escribiendo Lisp, Emacs es muy simple;
pero oculta bajo ella existe una complejidad que hace todo el trabajo.
RESUMEN
89
defvar y un asterisco
En el pasado, Emacs utilizaba defvar para variables internas que no se espera
que el usuario cambie y para variables que se espera que cambie el usuario.
Aunque puedes utilizar defvar para tus variables, se recomienda que utilices
defcustom en su lugar.
Cuando se especifica una variable utilizando defvar, puedes diferenciar la variable
que un usuario puede modificar de otras poniendo un * en la primera columna
de la cadena de documentacin. Por ejemplo:
(defvar shell-command-default-error-buffer nil
"*Buffer name for `shell-command' ... error output.
... ")
Puedes (y debes) utilizar set-variable para cambiar temporalmente el valor
de shell-command-default-error-buffer. Sin embargo, las opciones establecidas mediante set-variable slo duran durante una sesin de edicin. Los
nuevos valores no se guardan entre sesiones. A no ser que cambies el valor en el
fichero .emacs.
Para el autor, el principal uso del comando set-variable es que pulsando
M-x set-variable se muestra una lista completa de variables que se pueden
modificar.
Resumen
car
cdr car devuelve el primer elemento de una lista; cdr devuelve el segundo y
siguientes elementos de una lista.
Por ejemplo:
(car '(1 2 3 4 5 6 7))
1
90
cons construye una lista poniendo el primer argumento delante del segundo
elemento.
Por ejemplo:
(cons 1 '(2 3 4))
(1 2 3 4)
funcall funcall evala su primer argumento como una funcin. Pasa el resto
de argumentos a su primer argumento.
nthcdr Devuelve el resultado de tomar el CDR N veces en una lista. Se obtiene
el resto del resto.
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 resto
de la lista.
Por ejemplo:
(set triple '(1 2 3))
(setcar triple '37)
triple
(37 2 3)
(setcdr triple '("foo" "bar"))
triple
(37 "foo" "bar")
progn Evala cada argumento en una secuencia y luego devuelve el valor del
ltimo.
Por ejemplo:
(progn 1 2 3 4)
4
save-restriction Almacena cuando est activo el narrowing en el buffer actual,
si hay, y restaura el narrowing despus de evaluar los argumentos.
search-forward Busca una cadena y si se encuentra mueve el punto. Con una
expresin regular, utilizar la similar re-search-forward.
EJERCICIN DE BSQUEDA
91
Ejercicin de bsqueda
Escribe una funcin interactiva que busque una cadena. Si se encuentra
la cadena, lleve el punto tras ella y muestre un mensaje Encontrado!.
(Sin utilizar search-forward para el nombre de esta funcin; si lo haces,
sobreescribirs la versin existente de search-forward que viene con
Emacs. Utiliza un nombre como test-search en su lugar).
Escribe una funcin que muestre el tercer elemento del anillo de matados
en el rea de eco, si hay alguno; si el anillo de matados no contiene un
tercer elemento, que muestre un mensaje apropiado.
92
Chapter 11
94
95
96
Sin embargo, eso no cambia el valor del smbolo flowers como puedes ver
evaluando lo siguiente,
(eq (cdr (cdr bouquet)) flowers)
que devolver t por true.
Hasta que se resetee, flowers mantendr el valor (violet buttercup).
De este modo, en Lisp, al obtener el CDR de una lista, lo que se obtiene la
direccin de la siguiente celda (cell) de la serie; obtener el CAR de una lista, se
obtiene la direccin del primer elemento de la lista; con cons y un nuevo elemento,
aades una nueva cons cell delante de la lista. La estructura subyacente de Lisp
es brillantemente simple!
EJERCICIO
97
Ejercicio
Establecer flowers a violet y buttercup. Hacer cons con dos flores ms en
la lista y establecer esta nueva lista a more-flowers. Establecer el CAR de
flowers a un pez. Qu contiene ahora more-flowers?
98
Chapter 12
100
La variable kill-ring-yank-pointer
kill-ring-yank-pointer es una variable, igual que kill-ring.
Pongamos que el valor del anillo de matados es:
("some text" "a different piece of text" "yet more text")
y kill-ring-yank-pointer apunta a la segunda posicin:
("a different piece of text" "yet more text")
Un diagrama:
Chapter 13
Bucles y recursin
Emacs Lisp tiene dos maneras principales para hacer que una expresin, o una
serie de expresiones, se evale repetidamente: una es utilizar un bucle while y
la otra utilizar recursin.
La repeticin es muy valiosa. La gente escribe principalmente funciones Emacs
Lisp utilizando bucles while; pero puedes utilizar recursin, que proporciona
una manera verdaderamente potente de pensar y resolver problemas1 .
while
La forma especial while comprueba cuando el valor devuelto por la evaluacin
de su primer argumento es true o false. Es similar a como el intrprete Lisp
hace con if.
En una expresin while, si el valor devuelto evaluando el primer argumento es
falsa, el intrprete Lisp salta el resto de la expresin (el cuerpo de la expresin)
y no lo evala. Sin embargo, si la expresin es cierta (true), el intrprete
Lisp evala el cuerpo de la expresin y despus comprueba de nuevo el primer
elemento.
La plantilla while es:
(while TRUE-OR-FALSE-TEST
BODY...)
1 Emacs se ha diseado para correr en mquinas que se consideran limitadas y por defecto su configuracin es conservadora. Es posible que quieras incrementar los valores de
max-specpdl-size y max-lisp-eval-depth. Se podran aumentar en el fichero .emacs a 15 y
30, por ejemplo.
101
102
WHILE
103
Un ejemplo: print-elements-of-list
La funcin print-elements-of-list ilustra un bucle while con una lista.
La funcin necesita varias lneas para su salida. Puedes hacer la prueba en el
buffer *scratch*, por ejemplo.
(setq animals '(gazelle giraffe lion tiger))
(defun print-elements-of-list (list)
"Print each element of LIST on a line of its own."
(while list
(print (car list))
(setq list (cdr list))))
(print-elements-of-list animals)
Cada elemento se escribe en una lnea (que es lo que la funcin print hace). En
la ltima expresin de la funcin del bucle while se evala un nil y es lo que se
imprime despus del ltimo elemento de la lista.
104
; true-or-false-test
; incremento
Hay que observar que se debe establecer el valor inicial de count; normalmente
a 1.
ejemplo con un contador incrementado
Supn que ests jugando en la playa y decides hacer un tringulo de guijarros,
poniendo una piedrecita en la primera fila, dos en la segunda, tres en la tercera:
*
* *
* * *
* * * *
Cuntas piedras necesitas para hacer un tringulo de 7 filas?
Claramente, lo que necesitas es sumar los nmeros de 1 a 7. Lo que se puede
hacer fcilmente con un bucle while con contador.
Partes de la definicin de funcin
El anlisis anterior se llega a la conclusin que se necesita una variable total
para guardar el nmero total de piedras que ser lo que devuelva la funcin.
Segundo, sabemos que la funcin necesita un parmetro que diga cuntas filas
tendr el tringulo. Se puede llamar number-of-rows.
Finalmente, necesitamos una variable que acte como contador. Podemos utilizar
counter, pero un nombre mejor sera row-number.
105
WHILE
; true-or-false-test
; incrementar
Esto se complementara con la expresin let que establecera los valores de las
variables. Sin embargo, necesitar un elemento final que devuelva el valor de
total.
El toque final es poner la variable total en una lnea, sola tras la expresin
while. El pseudocdigo de la funcin sera:
(defun NOMBRE-DE-FUNCIN (LISTA-ARGUMENTOS)
"DOCUMENTACIN..."
(let (VARLIST)
(while (TRUE-OR-FALSE-TEST)
106
; true-or-false-test
; decrementar
WHILE
107
108
El macro dolist
Para darle la vuelta a una lista, por ejemplo que primero segundo tercero
se convierta en tercero segundo primero.
En la practica se utiliza la funcin reverse as:
(setq animals '(gazelle giraffe lion tiger))
(reverse animals)
As podramos revertir una lista utilizando un bucle while:
109
El macro dotimes
El macro dotimes es similar a dolist, excepto que hace el bucle un nmero
especfico de veces.
El primer argumento de dotimes es una variable que contendr el nmero. Un
ejemplo:
(let (value)
; otherwise a value is a void variable
(dotimes (number 3 value)
(setq value (cons number value))))
110
(2 1 0)
dotimes devuelve value, as la manera de utilizar dotimes es operar en alguna
expresin con el NUMBER de veces y devolver el resultado.
Aqu un ejemplo de defun utilizando dotimes para sumar el nmero de piedras
en un tringulo.
(defun triangle-using-dotimes (number-of-rows)
"Using `dotimes', add up the number of pebbles in a triangle."
(let ((total 0)) ; otherwise a total is a void variable
(dotimes (number number-of-rows total)
(setq total (+ total (1+ number))))))
(triangle-using-dotimes 4)
Recursin
Una funcin rescursiva contiene cdigo que le dice al intrprete Lisp que al
mismo programa que est corriendo pero con argumentos ligeramente diferentes.
Sin embargo, no es la misma instancia. En la jerga es una instancia diferente.
Si el programa est correctamente escrito, las diferencias de los argumentos son
suficientes para que desde el primer argumento llegue hasta la instancia final y
pare.
RECURSIN
111
112
its own.
do-again-test
body
recursive call
next-step-expression
(print-elements-recursively animals)
La funcin print-elements-recursively primero prueba cundo hay algn
contenido en la lista. Si hay algo, la funcin escribe el primer elemento de la
lista (el CAR). Despus, la funcin se invoca a s misma pero proporcionando
como argumento el CDR de la lista que le pasaron a esa instancia.
Dicho de otro modo, si la lista no est vaca, la funcin invoca otra instancia
con diferentes argumentos.
De otra manera: si la lista no est vaca, el primer robot monta un segundo
robot dicindole qu tiene que hacer. El segundo robot es un individuo distinto,
pero del mismo modelo.
Cuando la segunda evaluacin ocurre, la expresin when se evala. Si es true
se escribe el primer elemento de la lista que recibe en su argumento (que es el
segundo de la lista original). Luego se llama de nuevo con el CDR de la lista
que recibi (que ser el CDR del CDR de la lista original).
Eventualmente, la funcin se llamar con una lista vaca. Crear una nueva
instancia con un argumento nil. La expresin when evaluar list y la encontrar
false, de ese modo no ejecutar la clusula y devolver nil como un todo. No
habr ms repeticiones.
113
RECURSIN
(+ number
(triangle-recursively
(1- number)))))
; else-part
; recursive call
; next-step-expression
(triangle-recursively 7)
Un argumento de 1 2
Primero, qu ocurre cuando el valor del argumento es 1?
La funcin tiene una expresin if despus de la cadena de documentacin.
Prueba cuando el valor de number es igual a 1; si lo es se evala la then-part
que es igual a 1 (devuelve 1 sin ms recursin).
La else-part consiste en una suma, que llama recursivamente a
triangle-recursively y decrementa number.
(+ number (triangle-recursively (1- number)))
Cuando Emacs evala esta expresin, la expresin ms interna se evala primero;
luego las otras partes en secuencia.
Paso 1 Evala la expresin ms interna.
La expresin ms interna es (1- number) por lo que Emacs decrementa el
valor de number de 2 a 1.
Paso 2 Evaluacin de la funcin triangle-recursively.
Le intrprete de Lisp crea una instancia individual de triangle-recursively.
Emacs pasa el resultado del Paso 1 como argumento para esta instancia
de la funcin.
En este caso, Emacs evala triangle-recursively con un argumento de
1. Esto significa que la evaluacin devolver 1.
Paso 3 Evaluar el valor de number.
La variable number es el segundo elemento de la lista que comienza con +;
su valor es 2.
Paso 4 Evaluar la expresin +.
La expresin + recibe dos argumentos, el primero la evaluacin de number
(Paso 3) y el segundo la evaluacin de triangle-recursively (Paso 2).
El resultado de la suma es la suma de 2 ms 1 y se devuelve 3, lo que es
correcto. Un tringulo con dos filas tiene tres piedras en l.
114
Un argumento de 3 4
Supongamos que se llama triangle-recursively con un argumento de 3.
Paso 1 Evaluar do-again-test.
La expresin if se evala primero. La prueba do-again devuelve false,
por lo que se evaluar la else-part.
Paso 2 Evaluar la expresin ms interna de la else-part.
La expresin ms interna se evaluar decrementando 3 a 2. Estas es la
next-step-expression.
Paso 3 Evala la funcin triangle-recursively.
Se pasa 2 a la funcin trinagle-recursively.
Ya hemos visto en el punto anterior que cuando se llama a
triangle-recursively con un argumento de 2. Despus de toda
esa secuencia, devolver un 3. Que es lo que suceder aqu.
Paso 4 Evaluar la suma.
El 3 recibido como argumento se pasar a la suma que ser aadido a lo
que devuelve la llamada recursiva, que es 3.
El valor devuelto por la funcin al final, ser 6.
Ahora que sabemos qu suceder cuando se llama triangle-recursively con
un argumento 3, es evidente lo que ocurrir si la llamamos con un argumento 4:
En la llamada recursiva, la evaluacin de
(triangle-recursively (1- 4))
devolver el valor evaluando
(triangle-recursively 3)
que ser 6 y a ese valor se sumar 4.
RECURSIN
115
(cond
CUERPO...)
Donde el CUERPO es una serie de listas. Escrita de forma ms completa, la
plantilla sera:
(cond
(PRIMERA-PRUEBA-CIERTO-FALSO PRIMERA-CONSECUENCIA)
(SEGUNDA-PRUEBA-CIERTO-FALSO SEGUNDA-CONSECUENCIA)
(TERCERA-PRUEBA-CIERTO-FALSO TERCERA-CONSECUENCIA)
...)
Cuando el intrprete evala la expresin cond, evala el primer elemento de la
primera expresin. Si se evala como nil se salta a la siguiente expresin. Si
ninguna de las condiciones se evalan como true, la expresin cond devuelve
nil.
Escribir la funcin triangle utilizando cond sera algo as:
(defun tirangle-using-cond (number)
(cond ((<= number 0) 0=
((= number 1) 1)
((> number 1)
(+ number (triangle-using-cond (1- number)))))))
En este ejemplo, cond revuelve 0 si el nmero es menor o igual a 0, devuelve 1
si el nmero es 1 y evala (+number (triangle-using-cond (1- number)))
si el nmero es mayo que 1.
Patrones recursivos
Aqu veremos tres patrones recursivos. Cada uno implica las listas. La recursin
no necesita implicar listas, pero Lisp est diseado para las listas y estas dan
sentido a sus capacidades intrnsecas.
Patrn recursivo: every
El patrn recursivo every (para cada) se disea una accin para cada elemento
de una lista.
El patrn bsico es:
Si la lista est vaca, devuelve nil.
Si no, comienza la accin con el comienzo de la lista (el CAR de la lista).
a travs de llamadas recursivas a la funcin con el resto (el CDR) de
la lista,
y, opcionalmente, combina la actuacin sobre el elemento, utilizando
cons con los resultados de actuar sobre el resto.
116
Un ejemplo:
(defun square-each (numbers-list)
"Square each of a NUMBERS LIST, recursively."
(if (not numbers-list)
; do-again-test
nil
(cons
(* (car numbers-list) (car numbers-list))
(square-each (cdr numbers-list)))))
; next-step-expression
(square-each '(1 2 3))
(1 4 9)
Si numbers-list est vaca no hace nada. Pero si tiene contenido, construye una
lista combinada con los cuadrados de cada nmero formada a base de llamadas
recursivas.
La funcin print-elements-recursively es otro ejemplo del patrn every,
excepto que en este caso en lugar de utilizar cons para juntar los resultados, se
imprime cada elemento.
(setq animals '(gazelle giraffe lion tiger))
(defun print-elements-recursively (list)
"Print each element of LIST on a line of its own.
Uses recursion."
(when list
; do-again-test
(print (car list))
; body
(print-elements-recursively
; recursive call
(cdr list))))
(print-elements-recursively animals)
El patrn para print-elements-recursively es:
Cuando la lista est vaca no hace nada.
Cuando la lista tiene al menos un elemento,
Acta sobre el principio de la lista (el CAR).
Hace una llamada recursiva sobre el resto (el CDR) de la lista.
Patrn recursivo: accumulate
Otro patrn recursivo es el patrn accumulate. Este consiste en que la accin
que se realiza en cada elemento de la lista, se acumula produciendo un nico
valor para la accin completa.
El patrn es:
Si una lista est vaca, devuelve cero u otro valor constante.
RECURSIN
117
118
RECURSIN
119
esperando. Esto no es un problema cuando hay unos pocos pasos, como en este
ejemplo. Pero puede ser un problema cuando hay muchos ms pasos.
120
(triangle-recursive-helper
(+ sum counter)
; sum ms counter sum
(1+ counter)
; incrementar counter counter
number)
; number contina igual.
En la primera computacin:
(triangle-recursive-helper (+ 0 0)
(1+ 0)
1)
; sum
; counter
; number
que ser:
(triangle-recursive-helper 0 1 1)
Todava (> counter number) ser falso, por eso de nuevo el intrprete de Lisp
evaluar triangle-recursive-helper, creando una nueva instancia con nuevos
argumentos.
(triangle-recursive-helper
(+ sum counter)
; sum ms counter sum
(1+ counter)
; incrementar counter counter
number)
; number contina igual.
que ser:
(triangle-recursive-helper 1 2 1)
En este caso (> counter number) ser true. As la instancia devolver el valor
de sum, que ser 1, como se esperaba.
Ahora, veamos triangle-initialization con un argumento de 2, para encontrar cuantas piedras hay en un tringulo con dos filas.
Esta funcin llama (triangle-recursive-helper 0 0 2).
Por pasos, las instancias llamadas sern:
sum counter number
(triangle-recursive-helper 0
1
2)
(triangle-recursive-helper 1
2
2)
(triangle-recursive-helper 3
3
2)
Cuando se llama a la ltima instancia, la prueba (> counter number) ser
evaluado como true, as la instancia devolver sum que ser 3.
Este patrn ayudar a escribir funciones que salven muchos recursos del ordenador.
EJERCICIOS DE BUCLES
121
Ejercicios de bucles
Escribe una funcin similar a triangle en la que cada fila tenga el cuadrado
del nmero de fila. Utiliza un bucle while.
Escribe una funcin similar a triangle que multiplique en lugar de sumar
los valores.
Reescribe estas dos funciones recursivamente. Utiliza cond para hacerlo.
Escribe una funcin para el modo Texinfo que cree una entrada de ndice
al principio de un prrafo para cada @dfn en el prrafo. (En un fichero
Texinfo, @dfn marca una definicin.)
122
Chapter 14
Bsquedas de las
expresiones regulares
Las bsquedas con expresiones regulares se utilizan extensivamente en GNU
emacs. Las dos funciones, forward-sentence y forward-paragraph ilustran
bien esas bsquedas. La frase expresin regular frecuentemente se escribe
como regexp.
124
\\|
\\)[
La funcin re-search-forward
La funcin re-search-forward es muy similar a la funcin search-forward.
re-search-forward busca una expresin regular. Si la bsqueda tiene xito,
deja el punto inmediatamente despus del ltimo carcter del objetivo. Si la
bsqueda es hacia atrs, deja el punto inmediatamente antes del primer carcter
del objetivo.
Como search-forward, la funcin re-search-forward toma tres argumentos:
1. El primer argumento es la expresin regular que utilizar la funcin para
buscar. La expresin regular debe ser una cadena entre comillas.
2. El segundo argumento es opcional y limita cunto de lejos debe buscar; es
un lmite que se especifica como una posicin en el buffer.
3. El tercer argumento es opcional y especifica como responde la funcin al
fallo: nil como tercer argumento causa que la funcin lance una seal de
error cuando la bsqueda falla; cualquier otro valor hace que devuelva nil
si falla la bsqueda y t si tiene xito.
FORWARD-SENTENCE
125
forward-sentence
El comando para mover el cursor hacia adelante una sentencia es una forma
de ilustrar una bsqueda con expresiones regulares en Emacs Lisp. La funcin
parece ms larga y complicada de lo que debera, esto es porque est diseada
para ir hacia adelante tanto como hacia atrs y opcionalmente ms de una
sentencia. La funcin normalmente est asociada a la combinacin M-e.
126
El esqueleto:
(defun forward-sentence (&optional arg)
"DOCUMENTACIN..."
(interactive "p")
(or arg (setq arg 1))
(let ((opoint (point)) (sentence-end (sentence-end)))
(while (< arg 0)
(let ((pos (point))
(par-beg (save-excursion (start-of-paragraph-text) (point))))
RESTO-DE-CUERPO-DEL-BUCLE-WHILE-CUANDO-TRABAJA-HACIA-ATRS
(while (> arg 0)
(let ((par-end (save-excursion (end-of-paragraph-text) (point))))
RESTO-DE-CUERPO-DEL-BUCLE-WHILE-CUANDO-TRABAJA-HACIA-DELANTE
FORMAS-MANEJADORAS-Y-EQUIVALENTES
Esto parece ms simple. La definicin de la funcin consiste en documentacin,
expresin interactiva, una expresin or, una expresin let y un par de bucles
while.
La funcin tiene una declaracin interactive "p". Esto significa que el proceso
puede tener un argumento prefijo, que debe ser un nmero. La expresin or
comprueba si se ha pasado argumento y si no lo hay, lo pone a 1. Cuando se
pasa sin argumento arg est establecido a nil.
Lo siguiente es un let que establece los valores de dos variables locales, opoint
y sentence-end. La primera es establece al valor actual del punto antes de la
bsqueda. La segunda se establece a la funcin sentence-end.
127
FORWARD-SENTENCE
ELSE-PART
(setq arg (1- arg))))
128
; backward-moving-code
; forward-moving-code
129
Las primeras partes de la funcin son rutinarias: los argumentos (uno opcional),
la documentacin, la declaracin interactive con p (que procesa un argumento
numrico). La expresin or en la siguiente lnea maneja el caso en que no se
pase argumento a la funcin.
La expresin let*
La siguiente lnea comienza con la expresin let*, que es diferente de let. Es
similar a la versin sin asterisco excepto que Emacs establece cada variable
en secuencia una despus de otra y las variables en las siguientes partes de la
varlist pueden hacer uso de los valores definidos antes.
En la expresin let* Emacs asigna un total de siete variables: opoint,
fill-prefix-regexp, parstart, parsep, sp-parstart, start y found-start.
La variable parsep aparece dos veces, primero para eliminar instancias de y
despus para manejar los prefijos de relleno.
La variable opoint se establece al valor de point.
La variable fill-prefix-regexp se establece al valor devuelto por la evaluacin
de la siguiente lista:
(and fill-prefix
(not (equal fill-prefix ""))
(not paragraph-ignore-fill-prefix)
(regexp-quote fill-prefix))
Esta expresin utiliza la forma especial and, que evala cada uno de sus argumentos hasta que encuentra un valor nil, en cuyo caso la expresin devolver
nil; por contra, si ninguno de los argumentos devuelve el valor nil, el valor
resultante de la evaluacin ser el ltimo argumento evaluado. En otras palabras,
una expresin and devolver true slo si todos sus argumentos son true.
En este caso, la variable fill-prefix-regexp se establecer a un valor no
nil slo si las siguientes cuatro expresiones producen un valor true cuando se
evalen.
fill-prefix Cuando se evala esta variable, se devuelve el valor de la misma,
si tiene alguno. Si no hay prefijo de relleno se devolver nil.
(not (equal fill-previx "") Esta expresin comprueba cundo un prefijo
de relleno es una cadena vaca. Una cadena vaca no es un prefijo de relleno
usable.
(not paragraph-ignore-fill-prefix) Esta expresin devolver nil si se ha
activado la variable paragraph-ignore-fill-prefix o se le ha dado un
valor t.
(regexp-quote fill-prefix) Este es el ltimo argumento de la forma especial
and. Si todos los anteriores son true el valor resultante de esta expre-
130
131
(if fill-prefix-regexp
;; There is a fill prefix; it overrides parstart;
;; we go forward line by line
(while (and (not (eobp))
(progn (move-to-left-margin) (not (eobp)))
(not (looking-at parsep))
(looking-at fill-prefix-regexp))
(forward-line 1))
;; There is no fill prefix;
;; we go forward character by character
(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 parstart))
(or (not (looking-at parstart))
(and use-hard-newlines
(not (get-text-property (1- start) 'hard)))))
(forward-char 1))
;; and if there is no fill prefix and if we are not at the end,
;;
go to whatever was found in the regular expression search
;;
for sp-parstart
(if (< (point) (point-max))
(goto-char start))))
Se puede ver que es un bucle de contador decreciente, utilizando la expresin
(setq arg (1- arg)). Esta expresin no est muy alejada del while, pero se
esconde en otro macro de Lisp: unless. A no ser (unless) que estemos en el
final del buffer (que es lo que evala la funcin eobp) que es una abreviacin de
End Of Buffer P, el valor de arg se decrementa en 1.
Nos interesa que el contador de bucle no se decrementar hasta que deje el espacio
entre prrafos, a no ser que estemos al final del buffer donde no encontraremos
separador de prrafos.
El segundo while tambin tiene una expresin (move-to-left-margin). La
funcin se explica sola. Lleva una expresin progn y no es el ltimo elemento de
su cuerpo, por lo que slo se invocar por sus efectos colaterales, que es mover
el puntero al margen izquierdo de la lnea actual.
La funcin looking-at devuelve true si el texto tras el puntero coincide con la
expresin regular dada en su argumento.
El resto del cupero del bucle parece difcil, pero es sencillo comprenderlo.
Primero vamos a considerar qu ocurre con el prefijo de relleno:
132
(if fill-prefix-regexp
;; There is a fill prefix; it overrides parstart;
;; we go forward line by line
(while (and (not (eobp))
(progn (move-to-left-margin) (not (eobp)))
(not (looking-at parsep))
(looking-at fill-prefix-regexp))
(forward-line 1))
Esta expresin mueve el puntero lnea a lnea mientras estas cuatro condiciones
son true:
1. El punto no est al final del buffer.
2. Podemos movernos al margen izquierdo del texto y no estamos al final del
buffer.
3. El texto donde est el punto no es un separador de prrafos.
4. El patrn que sigue al punto es el patrn de relleno.
La ltima condicin puede ser confusa, hasta que recuerdas que el punto se ha
movido al principio de la lnea en la funcin forward-paragraph. Esto significa
que el texto tiene un prefijo de relleno, que es lo que mira la funcin looking-at.
Consideremos qu ocurre cuando no hay prefijo de relleno.
(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 busca hacia adelante una sp-parstart que es una combinacin
de posibles espacios en blanco con el valor local de inicio de un prrafo o de un
separador de prrafos.
Las dos expresiones,
(setq start (match-beginning 0))
(goto-char start)
significan que el inicio del texto coincide por la expresin regular buscada.
La expresin (match-beginning 0) es nueva. Devuelve un nmero que especifica
el lugar del inicio del texto que se ha encontrado con la ltima bsqueda.
La funcin match-beginning se utiliza aqu porque es una bsqueda hacia
adelante caracterstica. Una bsqueda que mueve el punto al final del texto que
RESUMEN
133
Resumen
Un breve resumen de las funciones que se acaban de presentar.
while Evala repetidamente el cuerpo de la expresin mientras el primer elemento sea evaluado como true. Luego devuelve nil. (La expresin se
evala slo por sus efectos colaterales.)
Por ejemplo:
(let ((foo 2))
(while (> foo 0)
(insert (format "foo is %d.\n" foo))
(setq foo (1- foo))))
re-search-forward Bsca un patrn y si lo encuentra mueve el puntero all.
Toma cuatro argumentos, como search-forward:
1. Una expresin regular que especifica el patrn de bsqueda. (Recuerda
poner marca de quotation para este argumento)
2. Opcionalmente, el lmite de la bsqueda.
3. Opcionalmente, qu hacer si la bsqueda falla, devuelve nil o un
mensaje de error.
4. Opcionalmente, cuntas veces repetir la bsqueda; si es negativa, la
bsqueda va hacia atrs.
let* Establece valores a variables locales y luego evala los restantes argumentos,
devolviendo el valor del ltimo. Mientras establece las variables locales,
usa los valores locales establecisas antes, si hay alguna.
Por ejemplo:
134
Chapter 15
Contando palabras
La distribucin estndar de Emacs contiene funciones de conteo de nmero de
lneas y palabras en una regin.
Ciertos tipos de escritura preguntan por el nmero de palabras. Por ejemplo,
al escribir un ensayo se pueden limitar las palabras a 800; si escribes una
novela, puedes imponerte la disciplina de escribir 1000 palabras diarias. La
gente normalmente utiliza Emacs para programar o escribir documentacin que
no suele requerer contar palabras. As se puede utilizar el comando wc del
sistema operativo. Alternativamente, la gente podra seguir la convencin de las
editoriales de dividir el nmero de caracteres por cinco.
La funcin count-word-example
Un comando de contar palabras en una lnea, prrafo, regin o buffer. Vamos a
disear una funcin de este tipo.
Claramente, contando palabras es una acto repetitivo: comenzar desde el inicio
de la regin, contar la primera palabra, luego la segunda, despus la tercera y
135
Diseando count-words-example
Primero, necesitamos implementar el contador de palabras en un bucle while.
El comando ser, por supuesto, interactivo.
La plantilla para una funcin interactiva es, como siempre:
(defun NOMBRE-DE-FUNCIN (LISTA-ARGUMENTOS)
"DOCUMENTACIN..."
(EXPRESIN-INTERACTIVA...)
CUERPO...)
El nombre de la funcin debera ser suficientemente descriptiva y similar al
nombre existente de count-lines-region. Eso har el nombre fcil de recordar. count-words-region es la opcin obvia. Pero ese nombre lo utiliza el
comando estndar de Emacs que cuenta palabras. As que el nombre ser
count-words-example.
La funcin contar palabras en una regin. Esto significa que la lista de argumentos debera contener smbolos que limiten las dos posiciones de inicio y final
de la regin. Esas dos posiciones se pueden llamar respectivamente beginning
y end. La primera lnea de la documentacin debe ser una sla frase, que ser
la que se muestre con el comando apropos. La expresin interactiva tendr la
forma (interactive "r"), porque eso har que Emacs pase el inicio y el final
de la regin a la lista de argumentos. El resto es rutina.
El cuerpo de la funcin necesita escribirse de tres tareas: primero, fijar las
condiciones del bucle while para contar palabras, segundo correr el bucle y
tercero, enviar un mensaje al usuario.
Cuando el usuario llame a count-words-example, el punto se mover al principio
o el final de la regin. Por esa razn, el cuerpo debe estar encerrado en una
expresin save-excursion.
La parte central del cuerpo de la funcin consiste en un bucle while en cuya
primera expresin salta el punto palabra a palabra y otra expresin que contar
esos saltos. La prueba true-or-false-test del bucle while debera ser true
mientras se pueda saltar hacia adelante, y falso cuando se encuentre el final de
la regin.
Usaremos (forward-word 1) como la expresin para mover el puntero palabra
a palabra, pero es fcil ver que Emacs identifica word utilizando un bsqueda
con una expresin regular.
Una expresin regular que busca un patrn deja el punto tras el ltimo carcter
coincidente. Esto significa una sucesin de bsquedas exitosas que movern el
LA FUNCIN COUNT-WORD-EXAMPLE
137
LA FUNCIN COUNT-WORD-EXAMPLE
139
141
143
Chapter 16
Contando palabras en un
defun
Nuestro siguiente proyecto es contar el nmero de palabras en una definicin de
funcin.
Divide y vencers
Descrito en una frase, el proyecto histograma es terrorfico; pero vamos a dividirlo
en muchos pequeos pasos, cada uno de los cuales haremos de una vez y el
proyecto se har ms llevadero. Vamos a considerar que los pasos podran ser:
Primero, escribir una funcin para contar las palabras en una definicin.
Esto incluye el problema de manejar smbolos tambin como palabras.
Segundo, escribir una funcin que liste el nmero de palabras en
cada funcin de un fichero. Esta funcin har uso de la anterior
count-words-in-defun.
Tercero, escribir una funcin que liste el nmero de palabras de cada funcin
en cada uno de varios ficheros. Esto incluye buscar automticamente en
varios ficheros, cambiar a ellos y contar las palabras en las definiciones que
contengan.
Cuarto, escribir una funcin que convierta la lista de nmeros que se ha
creado en el paso tres a un formulario que muestre un grfico.
Quinto, escribir una funcin que imprima los resultados como un grfico.
Es un gran proyecto, pero tomando cada paso despacio, no ser tan dificultoso.
145
146
Qu contar?
Cuando empezamos a pensar sobre cmo contar palabras en la definicin de una
funcin, la primera cuestin es (o debera ser) qu vamos a contar? Cuando
decimos palabras con respecto a una funcin en Lisp, estamos hablando, en su
mayora, de smbolos. Por ejemplo:
(defun multiply-by-seven (number)
"Multiply NUMBER by seven."
(* 7 number))
En este ejemplo, la funcin contiene los smbolos defun, multiply-by-seven,
number, * y 7. Adems, en la cadena de documentacin encontramos cuatro
palabras Multiply, NUMBER, by y seven. El smbolo number est repetido.
Sin embargo, si llamamos a la funcin que creamos en el punto anterior
count-words-example sobre ella, veremos que la definicin tiene once palabras
y no diez. Algo no funciona.
El problema es doble: count-words-example no contar * como una palabra
y sin embargo contar el smbolo multiply-by-seven como tres palabras. Los
guiones sern tratados como espacios entre palabras y no conectores intrapalabras.
La causa de esta confusin es la expresin regular que se utiliza para separar
palabras:
"\\w+\\W*"
LA FUNCIN COUNT-WORDS-IN-DEFUN
147
El \\( es la primera parte del grupo que incluye las partes \\w y \\s_ como
alternativas separadas por \\|. La parte \\w para caracteres que coinciden con
los que forman palabras y la \\s_ para los forman parte de un smbolo. El signo
+ indica que debe haber al menos un caracter que coincida.
Sin embargo, la segunda parte de la expresin regular es ms difcil de disear.
Al principio puede pensarse en definirla as:
"\\(\\W\\|\\S_\\)*"
Las W y S maysculas buscaran caracteres que no son partes de smbolos o
palabras. Desafortunadamente, esta expresin coincide con cualquier carcter.
Finalmente, necesitamos un patrn en que cada palabra o smbolo pueda estar
seguido por caracteres que no son espacios y luego por espacios. La expresin
regular sera:
"\\(\\w\\|\\s_\\)+[ \t\n]*[ \t\n]*"
La funcin count-words-in-defun
Como hemos visto hay varias formas de escribir la funcin, slo necesitamos
adaptar una de esas versiones a lo que queremos hacer.
La versin que utiliza el bucle while es quizs la ms clara y por eso es la que
vamos a utilizar. Esta sera su plantilla:
(defun count-words-in-defun ()
"DOCUMENTACIN..."
(SET UP...
(WHILE BUCLE ...)
DEVOLVER COUNT)
Vamos a rellenar las partes. Primero el set up.
Asumimos que la funcin se llamar en un buffer que contenga definiciones de
funciones. El punto puede estar en la definicin de una funcin o no. Para
que funcione count-words-in-defun el punto se debe mover al comienzo de la
definicin, establecer un contador a cero y el bucle debe parar cuando el punto
alcance el final de la definicin.
La funcin beginning-of-defun busca hacia atrs un delimitador ( al comienzo
de una lnea y mueve el punto a esa posicin, o al comienzo del lmite de la
bsqueda, si es el caso. In la prctica, significa que begining-of-defun mueve
el punto al comienzo de una definicin de funcin, por lo que podemos utilizarla
para empezar.
El bucle while necesita un contador que guarde las veces que se cuentan smbolos
o palabras. Una expresin let podra ayudarnos a crear una variable local pare
148
149
(message
"Counting words and symbols in function definition ... ")
(let ((count (count-words-in-defun)))
(cond
((zerop count)
(message
"The definition does NOT have any words or symbols."))
((= 1 count)
(message
"The definition has 1 word or symbol."))
(t
(message
"The definition has %d words or symbols." count)))))
Ahora, si evaluamos el cdigo de las dos funciones y lo probamos en la conocida
multiply-by-seven:
(defun multiply-by-seven (number)
"Multiply NUMBER by seven."
(* 7 number))
El valor devuelto ser 10, que es el valor buscado.
150
Buscar un fichero
Para buscar un fichero en Emacs, utilizas el comando C-x C-f (find-file).
Este comando no es el adecuado para el problema de los tamaos.
Si miramos en el cdigo de find-file:
(defun find-file (filename)
"Edit file FILENAME.
Switch to a buffer visiting file FILENAME,
creating one if none already exists."
(interactive "FFind file: ")
(switch-to-buffer (find-file-noselect filename)))
La definicin te muestra una corta pero completa documentacin y una especificacin interactiva que pregunta por un nombre de fichero cuando se usa
interactivamente el comando. El cuerpo de la definicin contiene dos funciones,
find-files-noselect y switch-to-buffer.
De acuerdo con su documentacin lo que hace es leer el nombre de un fichero,
cargarlo en un buffer y cambiar a l (En versiones ms modernas se permiten
el uso de caracteres especiales que permiten abrir varios a la vez, pero estos
argumentos opcionales son irrelevantes aqu).
Sin embargo, la funcin find-file-noselect no salta al buffer donde pone el
fichero. En el proyecto histograma, no necesitamos que se muestre por pantalla
cada fichero analizado. En lugar de utilizar switch-to-buffer utilizaremos
set-buffer que dirige la atencin del programa a un buffer diferente al que se
muestra por pantalla. Por eso, en lugar de llamar a find-file para hacer el
trabajo, debemos escribir nuestra propia expresin.
La tarea es fcil utilizando find-file-noselect y set-buffer.
lengths-list-file en detalle
El corazn de la funcin lengths-list-file es un bucle while que contiene
una funcin para mover el punto de defun en defun e ir llamando a funciones
auxiliares que cuenten el largo de cada una. La funcin debera ser:
(defun lengths-list-file (filename)
"Return list of definitions' lengths within FILE.
The returned list is a list of numbers.
Each number is the number of words or
LENGTHS-LIST-FILE EN DETALLE
151
152
lengths-list-many-files
El diseo utilizando bucle while es rutinario.
Chapter 17
Footnotes
153