Una de las muchas asignaturas pendientes que acarreo hace años es aprender algo de bash scripting, escribir programas de ejecución por lotes en sistemas GNU/Linux (el equivalente a los .bat de MS-DOS pero infinitamente más potentes). Así que últimamente me he animado a probar a hacer cosillas por practicar.

Uno de los comandos más útiles y completos con que cuentan los sistemas tipo unix es grep, un programa que busca en un archivo coincidencias con un patrón dado y las imprime por pantalla. El patrón no tiene por qué ser simplemente una palabra o un fragmento de la misma, la miga está en que también acepta expresiones regulares que, explicado de forma sencilla, son una manera lógica de expresar varias (o infinitas) cadenas de texto de forma reducida utilizando símbolos con ciertos significados. Es un mundo maravilloso una vez lo descubres y empiezas a comprender, así que si tienes interés visita el artículo de la wiki para saber más sobre el tema.

Esta semana estuve pensando cuántas palabras existirían en español que pudiesen escribirse alternando cada letra con una mano, escribiendo de forma correcta desde el punto de vista de la mecanografía. Por ejemplo: tutor, usual, blanco, airoso, soslaya, vividor, palenque, flamenco, ornamento, audiencia, antisocial, tormentoso, antiséptico, suboficial, digitiforme o ditirámbico. La palabra más larga en castellano que conozco es autosuficiencia, de 15 letras.

Hay que prestar especial atención a las palabras con tilde. El acento se escribe con la mano derecha, por lo que sólo son válidas las vocales de la mano izquierda, a y e.

¿Cuál es la expresión regular que nos ayudará a encontrar esto? Llamaremos izq las letras que se escriben con la mano izquierda izq={qwertasdfgzxcvb} y dch a las de la derecha, dch={yuiophjklñbnm}.

Para construir una expresión regular debemos ayudarnos de algunos símbolos con ciertos significados, por ejemplo el dólar $ representa el final de línea. Texto entre corchetes significa que todos los caracteres de su interior son válidos y hay que elegir uno de ellos. Un más (+) detrás de una expresión indica que hay que repetirla una o más veces (incluso infinitas). Por ejemplo, si tenemos [izq]+, las siguientes cadenas están representadas por esa expresión regular. ewt, qwezcvasdaq, dfgr… Hay algunas más como asterisco, interrogante, tubería o los paréntesis agrupan expresiones de forma general como en matemáticas. Si tenéis interés por internet hay cientos de páginas explicando estos detalles en profundidad. Una que me gusta especialmente es Regular-Expressions.

La primera aproximación sería decir:

^(([izq][dch])+|([dch][izq])+)$

Cualquier letra de la izquierda, seguida de otra de la derecha, una o más veces. O viceversa. Al envolverlo entre ^y $ indicamos que queremos que eso se cumpla para la palabra entera. Parece una buena aproximación, pero… ¡Sólo conseguimos palabras con número de letras par!

Para solucionarlo hay que pensar un poco… ¿Cómo se forman los impares? Pues cogiendo un par y añadiendo la unidad; y lo mismo haremos aquí. Tomamos la misma expresión que hemos utilizado antes y el añadimos una letra extra. La dejaremos como opcional utilizando el interrogante (elegir una o ninguna) y así no tenemos que escribir una expresión distinta para pares e impares.

^((([dch][izq])+[dch]?)|(([izq][dch])+[izq]?))$

Por ahora evitaremos los acentos, que complican las cosas. Tal y como está ya podemos empezar a buscar palabras ¿dónde? Pues toda distribución linux trae incorparada de serie un diccionario y se pueden instalar más. Bueno, más que diccionario es un listado de palabras que están en el diccionario, pero sin definiciones ni nada. En mi equipo están en la carpeta /usr/share/dict/ y tengo tres: spanish, british-english, y american-english, que contienen 86016, 98326 y 98569 entradas respectivamente.

Entonces, todo lo que tenemos que teclear en nuestro terminal es…

grep -E ‘^((([yuiophjklñnm][qwertasdfgzxcvb])+[yuiophjklñnm]?)|(([qwertasdfgzxcvb][yuiophjklñnm])+[qwertasdfgzxcvb]?))$’ /usr/share/dict/spanish

¡Pero cuidado! son más de mil palabras lo que escupirá esta expresión. Quizás queramos guardarlas en un archivo para leerlas con detenimiento (si al final de la expresión añades > nombreArchivo se guardarán en un fichero de texto). Otra maravilla que tienen los sistemas unix es la posibilidad de utilizar la salida de un comando como la entrada del siguiente utilizando el símbolo de la tubería (altgr 1) para separar ambos comandos. Así, si simplemente quieres contar cuántas salen, basta añadir al final del comando un simple | wc -l para que te lo indique. wc es un programa para contar palabras, pero con la opción -l cuenta líneas.

Y se pueden encadenar expresiones regulares sin parar. Poniendo al final | egrep ‘^.{5}$’ obtendremos todas las palabras de cinco letras. Y con | egrep ‘^.{10,}$ nos imprimirá solamente aquellas con 10 o más caracteres.

Por supuesto es un coñazo escribir la expresión regular cada vez que se quiera cambiar el conjunto de izquierda y derecha, por lo que he escrito un pequeño script en bash que nos genera la expresión regular y realiza la búsqueda automáticamente. Puedes verlo y descargarlo aquí. Con los dos primeros parámetros se indican ambos conjuntos; el tercer parámetro es opcional, y permite cambiar fácilmente de diccionario para comprobar esto mismo también en otros diccionarios. El cuarto argumento nos permite especificar que sólo se imprima la expresión regular en lugar de realizar la búsqueda. Si no se especifican al menos los dos primeros parámetros lanzará un mensaje explicando cómo ejecutarlo.

El tema de las tildes simplemente hace un poco más larga la expresión, pero se sigue basando en el mismo sistema. Habrá que tener en cuenta que el acento sólo puede ir después de una pulsación con la izquierda, y a continuación tendrá que ir una con la mano derecha. Y lo más importante: que no tiene por qué ocurrir siempre, por lo que ha de ser un parámetro opcional (usaremos el interrogante). Para facilitar la lectura la divido en dos lineas equivalentes, sólo que cada una representa un orden distinto. A lo mejor existe otra expresión regular más sencilla que lo simplifique, pero no se me ocurre.
‘^(([izq][áé]?)|(([izq][áé]?[dch])+|([izq][áé]?([dch][izq][áé]?)+))
|(([áé]?[dch])|([áé]?[dch][izq])+|([áé]?[dch]([izq][áé]?[dch])+)))$’

Al igual que antes, he escrito un script para que lo genere automáticamente y facilitar las pruebas con distintos conjuntos ver y descargar el script aquí. En este no preconfiguro diccionarios (los ingleses no tienen tildes después de todo), pero también puede cambiarse fácilmente a otro. Ejecutando el archivo sin suficientes parámetros mostrará la ayuda explicando esos detalles.

En el diccionario que tengo en mi equipo, que cuenta con 86 016 palabras, encuentro 1109, en la que la más larga es quelenquelen. Un amigo ha hecho pruebas con otro diccionario con algunas entradas más y obtiene 1307 (puedes ver la conversación aquí). He fusionado ambos archivos eliminando las repetidas y obtengo 1463 palabras que dejo aquí colgadas.

Una de las motivaciones que me han llevado a buscar esto era saber si se podría componer un texto que se pueda escribir alternando las manos ¿Alguien se atreve a escribir un texto utilizando únicamente este tipo de palabras?.

Nexos y palabras cortas que pueden ser útiles:

a, al, dos, e, el, él, en, ha, he, la, le, me, o, os, pro, que, se, si, su, sus, todo, tu, tus, u, vos, y, ya

Al estar la R, E y A en el mismo lado no aparecen verbos terminados en AR y ER, por lo que también he cogido palabras que cumplan la condición anterior, pero con esa terminación. No todas serán verbos, pero sí la mayoría. Este diccionario no tiene conjugaciones verbales, así que esto es una posible ayuda a todo aquel que se quiera atrever a escribir un texto, para facilitarle la búsqueda de las palabras apropiadas. Puedes consultar esta nueva lista aquí.