Retokenizando con spaCy

Para los que no están en contexto, spaCy es una librería de Python que provee funciones de PLN (procesamiento de lenguage natural, “NLP” en inglés) de una forma por demás fácil en comparación con, por ejemplo, NLTK.

Aunque conocía de su existencia, no había trabajado con spaCy hasta ahora que lo probé en el proyecto que tengo entre manos en el trabajo. Me era más familiar NLTK a pesar de haberlo usando en su mayoría en la versión 1 (van en la 3.5 al momento de escribir esto), pero para varias tareas spaCy es mucho más “directo”. Digamos que NLTK te da mucho poder, pero hay que ser mucho más específico al momento de manejarlo. En cambio, spaCy realiza muchas más acciones con menos interacción, lo cual puede ser bueno o extremo dependiendo del objetivo.

Lo interesante aquí para mí es nlp.pipeline. Una simple función, que por lo general la llaman nlp (pero uno puede definir el nombre),  aplica una serie de algoritmos de análisis y reconocimiento, pero antes realiza el proceso de “tokenizar” el texto, es decir, dividirlo en entidades llamadas “tokens”, que son secuencias de caracteres agrupados en unidades semánticas. Es fácil irse con la finta de que todos los tokens son palabras, pero no es así. Existen además diferentes maneras de tokenizar, y dependiendo de la usada es el resultado que se tendrá. Por ejemplo, una de las maneras más fácil de tokenizar es agrupando caracteres en un texto separado por espacios, como este post, por ejemplo. Obviamente una tokenización así no serviría en idiomas como el japonés, donde las palabras no están separadas por espacios, pero ésa es otra historia. También es necesario destacar que separar por espacios tampoco es una forma ideal de tokenizar, incluso lenguajes como inglés, pero en sí no se puede dar una respuesta correcta sin saber cuál es el objetivo final. De eso depende la forma de crear tokens.

En el caso del análisis que estaba realizando (en inglés), requería manejar palabras como “well-known”, “state-of-the-art”, es decir, palabras compuestas por múltiples otras palabras, unidas por un guión (entre otros casos que no necesito nombrar), como un token. El problema es que el tokenizer de spaCy separa las palabras también por guiones, y estos a su vez forman tokens. Por ejemplo:

“well-known”

es tokenizado como

“well”, “-“, “known”

Cada elemento es un token, así que contiene más que el simple texto: su función gramatical, su forma base, entre otras cosas, todo gracias a que spaCy ejecuta las funciones de reconocimiento y análisis después de la tokenización, pero todo sucede dentro del mismo pipeline. Además, como cada token es identificado por separado, casos como el de “state-of-the-art” deben ser tratados ya que la palabra completa es un adjetivo, pero “art” por sí mismo es correctamente identificado como sustantivo. Algo se tiene que hacer.

Continue reading “Retokenizando con spaCy”

Alma de estudiante

Ya es noviembre. Entre una cosa y otra, el año se pasa volando.

Hoy quiero hacer a un lado todas las entradas pendientes e incompletas que tengo para este blog y hablar un poco de lo que he hecho en poco más de un año.

En julio del año pasado viajé a Singapur, y en su momento escribí al respecto, pero no ahondé mucho en el tema de qué estaba haciendo por razones de manejo de información por acá. Ahora sí puedo al menos decir qué estoy haciendo sin temor a represalias por parte de la compañía en la que laboro.

El proyecto en el que estoy es sobre reconocimiento de escritura a mano usando redes neuronales, o para que suene más fancy, “Deep Learning“. Esa expresión ha cobrado mucha fuerza en los últimos años, al grado de haberse convertido en “Buzzword” y estar en boca de todos, aun en la de muchos que no tienen ni idea de qué onda. ¿No creen? Simplemente les menciono el ejemplo de una persona que quería ponerle Deep Learning a su sitio web para que fuera más llamativo…

Ahora bien: no es que yo haya estado alejado del área de aprendizaje máquina (ML – Machine Learning), puesto que cuando haces procesamiento de lenguaje natural usas algunas de sus técnicas (por ahí tengo un monstruo de código en Scala cuando quise implementar desde cero un clasificador bayesiano ingenuo), pero sí es la primera vez que trabajo con datos que no son palabras, y al mismo tiempo nunca me había metido tan de lleno a redes neuronales. Digamos que soy fan de las Support Vector Machine aunque las comencé a usar mucho después de haberme graduado. Por tanto, es la primera vez que me meto de lleno a las redes neuronales.

El caso es que he estado trabajando en ese proyecto desde hace más de un año, y aunque hemos obtenido avances significativos (nada todavía que sea digno de publicarse), sí tengo que mencionar que la burocracia japonesa (no necesariamente de esta compañía, sino de la forma en la que se hacen negocios en Japón) en general ha hecho que el proyecto vaya muy lento y que, como cualquier proyecto de software, deba de hacer ajustes a la mitad o cambios de esos que eran para ayer.

Ha habido muy buenas ganacias personales y profesionales a lo largo de este tiempo. Entre las más importantes puedo mencionar las siguientes:

  • Asistencia a conferencias importantes en el área de procesamiento de lenguaje natural. Este año he ido a 3: la de la sociedad de procesamiento de lenguaje natural de Japón en marzo, en Tsukuba; a la de la sociedad de inteligencia artificial de Japón en mayo; y a EMNLP 2017 en Dinamarca en septiembre. Sí, pisé tierras europeas por primera vez en mi vida. Esto conlleva también conocer a mucha gente, incluyendo a mexicanos que le están echando ganas en el extranjero dentro de la misma área.
  • Clase de Deep Learning en la Universidad de Tokio. Tomé la básica y ahora estoy tomando la avanzada, la cual termina en enero de 2018.

También es digno mencionarse que he aprendido a lidiar (léase: “perder toda la motivación”) con administradores de proyecto que se creen expertos en IA y que dicen cada sarta de p… aserciones incorrectas, y que en más de una ocasión me han ocasionado severos dolores de estómago porque a fin de cuentas dentro de la jerarquía japonesa ellos son los jefes y los de arriba solamente los escuchan a ellos… Experiencia amarga, sí, pero experiencia al fin y al cabo.

El título de esta entrada se refiere a que más que estar produciendo he estado del lado del aprendizaje, y ahora con el curso de la Universidad de Tokio ha revivido por completo el alma de estudiante, ya que el proyecto final es precisamente un sistema que use Deep Learning para algo, y todos mis compañeros de equipo son estudiantes de esa universidad; esto no me convierte inmediatamente en un líder, pero aquí sí comienza a pesar la experiencia de haber trabajado, sobre todo a la hora de dividir responsabilidades.

Con sus altibajos como casi cualquier cosa, más o menos en eso se ha resumido mi vida profesional desde julio de 2016 hasta la fecha. Así me he estado divirtiendo. No obstante, debo mencionar también que por obra de la misma burocracia mencionada arriba a veces pierdo todas las ganas de hacer las cosas y ha habido momentos en los que sí quiero mandar todo a la burger, pero luego recapacito, me tranquilizo y recuerdo que todavía no es el momento, pero que llegará.

Ahora nada más como dato cultural, dejo una breve explicación de lo que he aprendido y también referencias para quienes quieran leer más a detalle al respecto. Disculpen la terminología en inglés, pero es más fácil dar con información en ese idioma.

Primero que nada, omitiré todo lo referente a MNIST, puesto que hay infinidad de tutoriales y modelos que funcionan bastante bien. MNIST es como el “¡Hola mundo!” en ML, y curiosamente trata precisamente de reconocimiento de escritura a mano, aunque solamente de números. Si quieren probar por su cuenta, Tensorflow tiene un tutorial básico y uno avanzado al respecto.

Si buscan “Handwriting Recognition Deep Learning”, casi todos los papers que encuentren van a referenciar al trabajo de Alex Graves titulado “Connectionist Temporal Classification: Labelling Unsegmented Sequence Data with Recurrent Neural Networks”. Esta técnica ha hecho posible reconocer secuencias de caracteres sin necesidad de segmentarlos previamente. En sí, CTC puede ser aplicado al resultado de cualquier RNN, siendo hasta hace años las Bidirectional LSTMs , es decir: LSTM que no solamente toman en cuenta “el pasado”, sino también “el futuro”; conociendo más el contexto, los resultados obtenidos mejoran considerablemente; pero Alex Graves también presentó una alternativa a las BiLSTMs que mejoró todavía más los resultados: Las Multidimensional LSTMs (MDLSTMs), que lo que hacen es no nada más tomar en cuenta adelante y atrás de la secuencia analizada, sino también arriba, abajo, y cualquier otro punto en otra dimensión que colinde con el elemento analizado (una 2D MDLSTM analiza secuencias en 2 dimensiones – como imágenes; una 3D analiza secuencias en 3 dimensiones – como video, etc.). Gracias a que toma en cuenta mucho más contexto que las BiLSTM, las MDLSTMs presentan mejores resultados cuando son aplicadas correctamente.

Ahora bien, es muy importante tener datos para hacer que la computadora “aprenda”. Una buena base de datos para esta tarea es la de IAM. Es gratuita, solamente hay que registrarse para obtenerla.

He estado trabajando en diferentes frameworks:

Si se quieren quitar de broncas, Keras hace la vida muy sencilla. Lo usé durante un tiempo y, de entre los mencionados arriba, es mi framework favorito. Sin embargo, Keras es una librería de alto nivel que corre por encima de Tensorflow, CNTK o Theano, por lo que si necesitan hacer algun cambio significativo a bajo nivel, quizá no es opción.

Aprendí Tensorflow más motivado por conocer más profundamente las opciones que tiene que por obligación. Tensorflow es más difícil de digerir porque maneja un modelo de primero construir todo en grafos y estos se ejecutan solamente cuando es requerido, haciendo más difícil la depuración de los modelos (aunque justamente ayer Tensorflow sacó su versión experimental de Tensorflow Eager para evitar eso). Para que se den una idea muuuuy vaga, aprender Keras es como aprender Visual Basic (sin lo chafa, claro), y aprender Tensorflow es como aprender lenguaje C.

Torch es más por necesidad que por gusto. Aunque existe PyTorch, que es la versión de Python, el lenguaje original de Torch es Lua. Bien… otro lenguaje que aprender :/

En fin. La idea es que la computadora reconozca la escritura a mano de cierto lenguaje. Alex Graves presentó hace años un gran trabajo reconociendo escritura arábica, siendo que él no habla árabe. Las contribuciones de Alex Graves hacen posible omitir la segmentación de caracteres, puesto que los trabajos anteriores generalmente dependían de que cada carácter estuviera previamente segmentado y después se usaba algo como un diccionario o modelo de lenguaje para corregir posibles errores (se sigue haciendo y es recomendable, pero antes la dependencia era mayor).

Como ya he mencionado, aunque no he obtenido resultados dignos de ser publicados, ahí van las cosas. Al momento de escribir esto estoy relegado a hacer talacha en Lua (más programación que investigación), pero espero que sea solamente temporal. Y de pilón, también soy sysadmin de los servidores con los que trabajamos. Digamos que los demás aquí no tienen mucho conocimiento de Linux :/

Fcitx en Ubuntu 16.04 después de actualizar

Debido a que escribí en Twitter al respecto, y que en general no tengo que editar mucho cuando escribo cosas técnicas, decidí dejar esto por acá para la posteridad (y a ver si a alguien más le sirve).

Hace unos días, actualicé la máquina que uso en el trabajo. Tenía Ubuntu 14.04 y todo funcionaba bien, pero el día apenas comenzaba y yo estoy súper atorado en el proyecto en el que estoy, por lo que decidí darme un “descanso” y actualizar a 16.04.

Todo estuvo bien, salvo unos archivos de apt. Pero cuando llegué a la configuración del método para escribir (donde agrego lo necesario para escribir en japonés), tuve un problema: ibus no reaccionaba. Pensé que era buena oportunidad de cambiar a fcitx, ya que en casa lo instalé y no medio problemas. No fue el caso en la máquina del trabajo: fcitx funcionaba en la terminal y en algunas otras aplicaciones, pero en Chrome, Firefox o similares, nada. Tampoco en Gedit… y fue eso último que me llevó a pensar que quizás había un problema con esa configuración.

En mi .xsession, tenía configuradas estas 2 variables:


export GTK_IM_MODULE=xim
export QT_IM_MODULE=xim

(que si no hay razón para seguir usando XIM es harina de otro costal). Cambié a:


export GTK_IM_MODULE=fcitx
export QT_IM_MODULE=fcitx
export XMODIFIERS=@im=fcitx

Pero nada…Pasé un buen rato buscando algo de información, pero no daba con algo en concreto. Obviamente había buscando en la información oficial, pero no lo hice de nuevo después de pensar que GTK podría ser el problema.

Me encontré entonces en el FAQ de fcitx:

  • If you are using Ubuntu and upgrade to 12.04 recently, or something werid happens to your system (Due to packager careless, or buggy package manager which can not do upgrade in correct order, for example, pacman), you might notice that gtk.immodules related files doesn’t generate correctly during upgrade. Try uninstall fcitx-frontend-gtk2fcitx-frontend-gtk3 or coressponding package on your system and re-install them to trigger the file generate. Then recheck the input method menu to see whether it have “Fcitx” in the menu or not.

¡Ajá! Yo había actualizado a 16.04, pero los síntomas eran muy similares. Así que hice lo indicado:


$ sudo apt purge fcitx-frontend-gtk2 fcitx-frontend-gtk3

$ sudo apt install fcitx-frontend-gtk2 fcitx-frontend-gtk3

$ fcitx -r &

Ejecuté Gedit y, ¡bien! Ya podía escribir en japonés de nuevo. Pero seguía la prueba final: Google Chrome (o Vivaldi, que también lo uso)

成功!

Pasé día y medio teniendo que escribir en Emacs y copiando y pegando a Chrome cuando tenía que enviar algún correo en japonés. ¡Pero no más! 😀

Título de canción de Spotify en Xmobar

Aunque sé que Xmobar tiene Mpris1 y Mpris2, resulta que no lo instalé con soporte para ninguno, por lo que si quería poner el título de la canción que está siendo reproducida en Spotify, tenía que hacerlo a mano.

Tenía un buen rato de no hacer un script de estos. Quizá haya mejores alternativas, pero para algo que me tomó unos 20 minutos, creo que cumple su objetivo:

#!/bin/bash

spotify_pid=`pgrep spotify | head -1`

if [[ ! -z $spotify_pid ]]; then
   found=false
   while [ "$found" = false ] && IFS= read -r line; do
      pid=`echo $line | awk '{ print $3 }'`
      if [ "$pid" = "$spotify_pid" ]; then
         title=`echo $line | awk '{$1=$2=$3=$4=""; print $0 }' | tr -s ' '`
         echo "Spotify: ${title} | "
         found=true
      fi
   done < <(wmctrl -lp)
fi

El resultado:

Reflejar el área maestra en XMonad

Tengo 2 monitores en el trabajo. Mi silla está justo en medio de los 2, por lo que las áreas que más uso para trabajar son la mitad derecha del monitor izquierdo y la mitad izquierda del derecho.  Ahora bien: si han usado XMonad, sabrán que por lo general las áreas maestras de los layout están del lado izquierdo, lo que no me conviene para el monitor de ese lado, pero realmente nunca me había puesto a buscarle solución.

Hoy tuve un buen de ventanas abiertas y estuve revisando datos en todas, pero trabajando en ellos en el área maestra. De repente sentí la necesidad de tener la del monitor izquierdo en la parte derecha y me di a la tarea de investigar qué podía hacer.

Continue reading “Reflejar el área maestra en XMonad”

Detectando si hay monitor conectado en el puerto HDMI con Haskell

Generalmente hay 2 lugares en donde uso la laptop:

  • En mi escritorio, donde la conecto a un par de monitores, uno de ellos por HDMI).
  • En cualquier otro lugar.

Como uso XMonad, la posición de algunas barras personalizadas varía dependiendo de si estoy usando el monitor de la laptop o el de HDMI. El cambio siempre lo he he hecho manualmente comentando o habilitando un par de líneas en el xmonad.hs, pero quería automatizar el proceso de ser posible. Tenía rato que no usaba Haskell en forma, así que…

Básicamente, lo que necesito es hacer esto en Haskell:

xrandr | grep HDMI1 | cut -d " " -f2

Me eché un clavado en la documentación, y encontré la librería System.Process, concretamente la función createProcess, la cual está definida de la siguiente forma:

createProcess :: CreateProcess -> IO (Maybe Handle, Maybe Handle, Maybe Handle, ProcessHandle)

Donde CreateProcess está definido como se menciona acá:

http://hackage.haskell.org/package/process-1.2.0.0/docs/System-Process.html#t:CreateProcess

Lo que necesito entonces es crear un proceso por cada comando que quiero ejecutar, pero pasar los resultados de uno al siguiente (pipe).  Por tanto, necesito  declarar CreateProcess de la siguiente manera:

Continue reading “Detectando si hay monitor conectado en el puerto HDMI con Haskell”

Logrotate error en MySQL

Tenía ya tiempo recibiendo a diario esta notificación en los logs:

/etc/cron.daily/logrotate:
error: error running shared postrotate script for /var/log/mysql.log /var/log/mysql/mysql.log /var/log/mysql/mysql-slow.log
run-parts: /etc/cron.daily/logrotate exited with return code 1

Busqué en internet y todos los sitios mencionaban que la causa era que el password de la cuenta debian-sys-maint no era el que estaba indicado en el archivo /etc/mysql/debian.cnf (en Ubuntu):

[client]
host     = localhost
user     = debian-sys-maint
password = xxxxxxxxxxxxxxxx
socket   = /var/run/mysqld/mysqld.sock
[mysql_upgrade]
user     = debian-sys-maint
password = xxxxxxxxxxxxxxxx
socket   = /var/run/mysqld/mysqld.sock
basedir  = /usr

Cambié el password en la base de datos, pero nada. Todo seguía igual. Y al revisar el hash antes y después de cambiar el password era exactamente el mismo, lo que quería decir que el password estaba bien.

Usando ping en mysqladmin

mysqladmin --defaults-file=/etc/mysql/debian.cnf ping

me di cuenta de que no se podía se conectar por problemas del socket. Entonces entendí qué estaba pasando: el directorio en donde el socket de MySQL se crea está especificado en my.cnf, y lo tengo definido como /tmp/mysqld.sock . Le puse ese valor al archivo arriba mencionado, volví a correr el proceso y todo funcionó a la perfección.

Lo anterior pasó porque primero instalé MySQL “a pie”, y recuerdo que cambié varias configuraciones; después, tuve que instalar MySQL usando apt y usé el mismo archivo de configuración. Eso fue lo que causó el problema.

¿Por qué instalé con apt si ya tenía todo configurado bien a mano? Porque lo que instalé por mi cuenta fue para hacer correr un sistema que tenía configuraciones y versiones del año del caldo, y para poder reproducir el comportamiento original tenía que tener exactamente la misma configuración. Afortunadamente eso ya quedó en el pasado y no lo necesito más.

Sobreviviendo en Windows

Llevo semana y media en el nuevo trabajo, y lo que más me “duele” es que tengo que usar Windows 7 voluntariamente a fuerzas…

Dejando atrás el hecho de que muchos de los sistemas internos funcionan solamente con la fantabulosa combinación Windows + IE (del 7 para arriba) *pausa para que se les quiten las náuseas*… el hecho de tener que hacer reportes en Excel ya se me hace de lo más normal (sí, me siento sucio).

Hecho el respectivo rant…

He estado buscando la forma de dejar Windows lo más parecido posible a XMonad. Realmente los tiling window manager me hacen la vida más fácil, y al mismo tiempo me hacen más productivo porque no se pierde tiempo acomodando ventanas o buscándolas con el mouse. Total que después de navegar un rato, lo más cercano a XMonad a lo que he podido llegar es:

  1. mDesktop. Para crear los escritorios virtuales. Usa Alt como la tecla especial y se pueden crear hasta 10 escritorios.
  2. WinSplit Revolution. Para acomodar las ventanas en ciertas regiones de la pantalla. No es para nada tan funcional como XMonad, pero peor es nada. Tampoco se puede configurar mucho que digamos.
  3. Launchy. El launcher, para reemplazar a dmenu. De los 3 programas aquí mencionados es con el que más satisfecho estoy.

Sé que lo anterior no es remotamente nada similar a dwm o XMonad, pero algo es algo. Sigo todavía buscando opciones, sobre todo en lo que al acomodo de ventanas se refiere. Si alguien tiene sugerencias, son todas bien recibidas.

Para cliente de Twitter, he estado jugando con MetroTwit, y la verdad es que es muy recomendable. Linux carece de buenos programas en este rubro (yo uso Hotot). Sin embargo, hoy me recomendaron Janetter y también me gustó la interface. Es cuestión de decidirme por alguno. Pero aun así, estoy buscando uno que deje “tuitear” desde el system tray o un popup ahí cercano, porque eso de tener que abrir la ventana para mandar un tweet es una acción que prefiero evitar en el trabajo, y no es porque me regañen por estar en Twitter (no lo hacen. No está prohibido).

En lo que a navegadores se refiere, además del fatídico IE9 *pausa para que les quiten las náuseas*… ya instalé Firefox, Chrome y Opera, siendo este último el que estoy usando como “navegador personal”, mientras que los otros los tengo para pruebas. Tenía rato que no usaba Opera “seriamente”, y me dio gusto encontrar un navegador maduro con varias opciones interesantes. Si sólo Midori tuviera soporte para más tecnologías (entre ellas Flash), no dudaría en instalarlo (y sólo por el nombre, jeje).

Entre otras cosas que instalé:

  • WinScp
  • Virtual Clone Drive (para no extrañar el mount de ISO de Linux).
  • Notepad++
  • Haskell Platform
  • Leksah
  • Imgburn

Se aceptan tips de buenos programas para… dejen agarro aire… windows… que hagan más amena la experiencia.

Obteniendo todas las posibles “palabras” de 7 letras (o menos)

Últimamente, el juego al que más le dedico tiempo (además de Tekken) es a Angry Words (apalabrados en español). Con eso que me gusta Scrabble desde hace mucho, me relaja mientras voy en el tren y al mismo tiempo me ayuda a practicar mi vocabulario.

De la misma manera, le he estado dedicando tiempo a aprender Haskell, ya que veo que algunas cosas de PLN podrían ser implementadas más fácilmente con programación funcional.

Buscaba un problema que me ayudara a practicar Haskell, a dar mis primeros pinitos en el lenguaje. Y mientras disfrutaba el mencionado juego y veía que es más de “ponle lo que sea a ver si pega” en vez de “piensa en alguna palabra interesante”, se me ocurrió crear un programa que, dada una serie de letras, mostrara todas la combinaciones posibles entre ellas. Lo veía fácil y factible, por lo que me decidí a poner manos a la obra.

¿Qué tan difícil puede ser?… me pregunté. Y aunque no es algo tan complicado, sí me tomó más tiempo del que pensaba debido a que había que hacer el cambio a programación funcional y no a imperativa.

Primero, a definir el algoritmo:

Tomemos una palabra de 3 letras (por conveniencia, más adelante verán por qué), digamos “ola”. ¿Qué es lo que quiero obtener? La lista de posibles permutaciones con esas 3 letras, es decir: ola, oal, loa, lao, aol, alo. Aquí me di cuenta de un patrón: tomo una letra de la palabra, la pongo al principio, y simplemente tomo las otras 2 letras restantes y les cambio el orden. Para comenzar, tomo la “o” como la letra principal, entonces me quedo con “la”, y las únicas combinaciones posibles son “la” y “al”; después, agrego la letra principal “o” y la pongo al principio de cada palabra, obteniendo “ola”,”oal”. Continúo con la siguiente letra, la “l”, la tomo como principal, quedándome “oa”, de la cual obtengo “oa” y “ao”; le pongo la “l” al principio de cada una y obtengo “loa”,”lao”. Por último tomo la “a” como letra principal, lo que me deja con “ol” como el resto, obteniendo “ol” y “lo”; le pongo la “a” al principio de cada una y resultan “aol”, “alo”. Juntando todos los resultados parciales, obtengo la lista de palabras que estoy buscando.

Con la definición anterior, recursivamente se pueden analizar palabras de cualquier longitud. ¿Buenas noticias para los que juegan apalabrados? En teoría, si es que tienen la paciencia de ver todos los resultados. ¿Cuántos son? Saquemos cuentas:

Una palabra de 3 letras resultó en una lista de 6 palabras. ¿Cuántas resultarán de una de 4? Como lo que buscamos son permutaciones, la fórmula es sencilla:

n!

Revisemos: 3! = 3x2x1 = 6. ¡Bien! Si cuadra el resultado

Entonces, con 4 letras tendremos: 4! = 4x3x2x1 = 24 Todavía no son muchas…

Con 5 : 5! = 5x4x3x2x1 = 120 ya da flojera ver tantas permutaciones…

¿Y con 7? Pues 7! = 7x6x5x4x3x2x1 = 5040 Está bien que no haya límite de tiempo en cada jugada de apalabrados, pero qué flojera (o ganas de ganar) de aquél(la) que se ponga a leerla cada vez.

¡Ajá! Dirán ustedes: ¿qué pasa si hay letras repetidas? Obviamente habrá palabras repetidas también, lo cual reduce nuestra lista. ¿A cuánto? Oh benditas matemáticas:

n!/(a!b!c!…)

En donde a es el número de veces que se repite una letra, b es el número de veces que se repite otra, c es el número de veces que se repite otra… y así sucesivamente.

Supongamos que tenemos las letras “atajada” (si las tienen, ¡no la piensen y jueguen esa palabra!). La letra “a” se repite 4 veces, entonces, tendremos:

7!/4! = 7x6x5 = 210

Quizá pude haber pensado el algoritmo con una palabra de 4 letras desde el principio, y aunque sí revisé a mano después, me era más fácil iniciar con una de 3 (sólo 6 posibles permutaciones).

Luego, a hacer el programa. El resultado, a sabiendas de que puede haber una mucho mejor implementación, es una belleza de 18 líneas de código (y porque puse doble enter en algunos lugares):

Continue reading “Obteniendo todas las posibles “palabras” de 7 letras (o menos)”

XMonad: Cambiar el nombre del layout que se muestra

Hace tiempo escribí sobre XMonad y un xmonad.hs básico en donde mostraba cómo mostrar íconos en vez del título del layout. Esta vez esalgo similar, pero como ahora sí estoy estudiando Haskell en forma, ando buscando cómo hacer lo que quiero con guards:

En uno de los escritorios estoy usando LayoutCombinators para combinar 2 layouts (concretamente simpledTabbed y DragPane Horizontal). Al mostrarse el nombre del layout aparece lo siguiente:

“combining Tabbed Simplest with DragPane Horizontal 0.1 0.099999999”

Debido a su longitud, me quita espacio del título de la ventana activa, por lo que pensé hacer un shorten, pero después recordé que en la laptop hago pattern matching para mostrar un ícono en vez del nombre del layout, por lo que pensé hacer algo parecido: si el nombre del layout comienza con “combining”, muestro un nombre que yo defina, y en cualquier otro caso muestro el nombre original.

El layout quedó así:

myXmobarPP h = xmobarPP
             { ppOutput = hPutStrLn h
             , ppCurrent = xmobarColor "green" "" 
             , ppHidden = xmobarColor "#5f7962" ""
             , ppVisible = xmobarColor "#f4a460" ""
             , ppUrgent = xmobarColor "#cc0000" "" . wrap "*" "*"
             , ppSep = " | "
             , ppWsSep = "  "
             , ppLayout = xmobarColor "#5CBAF5" "" .
                          (\lName -> showLayoutName lName)
             , ppTitle = xmobarColor "white" "" . shorten 150
             }

showLayoutName :: String -> String  
showLayoutName a | Just rest <- stripPrefix "combining" a = "Tabbed + DragPane H" 
showLayoutName a = a

Donde lo que quiero hacer está en ppLayout: una expresión lambda para revisar si el título comienza con “combining” o no. Esa revisión la hago en la función showLayoutName con la función stripPrefix, que recibe una lista y regresa otra lista que contiene los elementos después del prefijo indicado si es que existe, o Nothing en caso contrario. Si el título del Layout comienza con “combining”, regreso “Tabbed + DragPane H”, y si no, simplemente regreso lo que me llegó.

Intenté hacerlo con guards, pero nomás no me compiló el código, y como estoy en el trabajo, no me dio más tiempo de probar, por eso lo implementé como aquí se muestra.

Si quisiera hacerlo en Scala, el código quedaría así:

val xmobarColor = (bgColor: String) => (fgColor: String) => (output: String) => {
  // Hacer algo con los parámetros y regresar un String
  ...
}

val showLayoutName = (layout: String) => {
  layout.startsWith("combining") match {
    case true => "Tabbed + DragPane H"
    case _ => layout
}

// Asignando todo a ppLayout

val ppLayout = ((xmobarColor("#5CBAF5")("")) compose showLayoutName)

// Siendo layoutName el nombre del layout actual

ppLayout(layout)

Aunque sigo siendo novato en Scala (y para esos fines, en programación funcional), me late más la forma concisa de Haskell. Hacer function composition en scala es agregar demasiadas palabras (aunque si usara scalaz todo cambiaría), mientras que en Haskell el currying, la aplicación parcial y la composición se hacen sin mayor complicación.

Sigo estudiando y practicando.