Revoltijo Elisp (III)
Una función que intercala las líneas de dos textos
Como sucede casi siempre, esta función nació de una necesidad concreta. Para la composición tipográfica de un libro en que andaba trabajando, tenía que intercalar los versos de un poema (traducción) con los de otro poema (original), de manera que el verso 1 del poema A fuera seguido por el verso 1 del poema B. Y así sucesivamente. De esta situación puntual podemos, por tanto, extraer un escenario más universal. A saber:
- Nos encontramos con dos textos (A y B) que tienen el mismo número de líneas (condición sine qua non para que nuestra función tenga sentido).
- Queremos conseguir una especie de copia/pega «inteligente», mediante el cual las líneas del texto B se intercalen con las del texto A: A1/B1, A2/B2, etc.
Aquí jugará un papel importante el alma de los Lisp, las listas, pues la gracia está en convertir ambos
textos en una lista y concatenarlas. Definimos una primera función para el texto de partida (el que queremos copiar, el
texto B), donde nos viene como anillo al dedo la expresión split-string
que nos permite crear una lista a partir de
una cadena, pero declarando qué tipo de elemento o corte ha de tenerse en cuenta para distinguir y separar los
componentes de la lista. En este caso, como queremos almacenar cada línea del texto en la lista, el corte ha de ser un
salto de línea (\n
):
(defun lineas-a-lista () "Esta primera función almacena en una lista cada línea de un texto marcado como elemento de la lista" (if (region-active-p) (progn (narrow-to-region (region-beginning) (region-end)) (split-string (buffer-string) "\n" nil)) (error "ninguna región marcada")))
La siguiente función se encarga de definir la variable lineas-b
y darle el valor de la lista de líneas del texto B:
(defun captura-lineas-a () "captura en una lista las líneas del primer texto que marcamos" (interactive) (setq lineas-b (lineas-a-lista)) (deactivate-mark) (widen))
Y, por último, llegamos a la función que se encarga de intercalar en el texto A las líneas capturadas mediante la
función anterior en el texto B. Generamos una tercera lista (lineas c
), cuyo valor será el de las dos listas
anteriores intercaladas. Aquí todo el trabajo lo hace la expresión -interleave
del paquete dash
(le sacamos jugo
también aquí), muy útil. Ya sólo quedaría eliminar el texto A e insertar en su lugar la nueva lista, convertida en una
nueva cadena.
(defun intercala-lineas () "intercala líneas en el siguiente texto marcado" (interactive) (setq lineas-a (lineas-a-lista)) (setq lineas-c (-interleave lineas-a lineas-b)) (replace-regexp ".+" "" nil (region-beginning) (region-end)) (deactivate-mark) (widen) (insert (mapconcat 'identity lineas-c "\n")))
Y como un gif vale más que mil palabras:
∞
Publicado: 04/10/2019
Última actualización: 16/08/23
Esta obra está bajo una licencia de Creative Commons Reconocimiento-NoComercial 4.0 Internacional.