El Blog de Webcu

Expresiones regulares de adolescentes (JS version)

November 19, 2019

En Exercism podemos encontrar muchos ejercicios interesantes. Uno que resulta muy atractivo para practicar nuestras habilidades con expresiones regulares, es el de modelar un conjunto de respuestas de un adolescente en función de las acciones de los adultos. El conjunto de respuestas es el siguiente:

Acción Respuesta
Le preguntan algo Seguro
Le gritan Relájate
Le preguntan algo gritando Calmate, yo sé lo que estoy haciendo
Si lo miran sin decirle nada Muy bien, sigue así
Cualquier otra acción Lo que digas

La primera tarea fue encontrar las expresiones regulares que me ayudaran a modelar las distintas acciones.

Veamos una por una, como podemos modelar las diferentes acciones:

  • Preguntar algo:

    Para saber que estamos preguntando en el idioma inglés la oración debe terminar con el signo de interrogación (?). Esta es la expresión regular que nos permite modelar una pregunta en ingles: /\?+\s*$/.

    • /.../ son los delimitadores de la expresión regular
    • La primera parte \?+ nos permite verificar que la oración contiene al menos un signo de interrogación.
\ Cuando la barra invertida se encuentra delante de un carácter especial lo trata como un carácter simple. Cuando se encuentra delante de un carácter simple lo trata como un carácter especial.
\? Para buscar por signos de interrogación.
+ Busca el carácter precedente 1 o más veces.
  • La segunda parte \s*$ nos permite aceptar la oración si termina con cero o más espacios en blanco. Cualquier otro carácter es rechazado.
\s Coincide con un carácter de espacio, entre ellos incluidos espacio, tab, salto de página, salto de línea y retorno de carro.
* Busca el carácter precedente cero o más veces.
$ Busca el final de la entrada.
  • Gritar:

    Para saber que estamos gritando todas las letras deben estar en mayúscula. Inicialmente modelar esta acción me resultó complicada, porque traté de hacerlo con una sola expresión regular pero finalmente terminé utilizando dos y es mucho más sencillo.

    • Una para saber si la oración contiene mayúsculas: /[A-Z]/.
    [A-Z] Grupo de caracteres de la A hasta la Z.
    • Otra para saber si la oración contiene minúscula: /[a-z]/.
    [a-z] Grupo de caracteres de la a hasta la z.

    Finalmente el código para saber si alguien está gritando queda así:

    if (/[A-Z]/.test(message) && !/[a-z]/.test(message)) {
      return 'Whoa, chill out!';
    }  

    Nota: Otra opción para saber si una oración contiene solamente mayúsculas es: convertir la oración a mayúsculas y compararla con la oración original. Si coinciden es porque la oración original ya estaba en mayusculas. Ex: string === string.toUpperCase().

  • Preguntar algo gritando:

    Esta acción la podemos modelar como una combinación de las dos anteriores:

    if (/[A-Z]/.test(message) && !/[a-z]/.test(message) && /\?+\s*$/.test(message)) {
    return 'Calm down, I know what I\'m doing!';
    }  
  • Mirar sin decir nada:

    En esta acción podemos reutilizar el conocimiento que ya tenemos acerca de buscar espacios en blanco, que vimos en la acción de preguntar. Esta es la expresión regular para modelar que una oración contiene solamente espacios en blanco: /^\s*$/.

    ^ Busca la inicio de la entrada
    \s Coincide con un carácter de espacio, entre ellos incluidos espacio, tab, salto de página, salto de línea y retorno de carro.
    * Busca el carácter precedente cero o más veces.
    $ Busca el final de la entrada.
  • Cualquier otra acción:

    Esta expresión regular es más difícil de modelar, pero no necesitamos realmente modelarla. La idea es testear las acciones anteriores y si ninguna se cumple entonces devolvemos: Whatever.

Solución inicial

export const hey = (message) => {
  if (/^\s*$/.test(message)) {
    return 'Fine. Be that way!';
  }

  if (/[A-Z]/.test(message)) {
    if (/^[^a-z]*\?+\s*$/.test(message)) {
      return 'Calm down, I know what I\'m doing!';
    }

    if (/^[^a-z]*$/.test(message)) {
      return 'Whoa, chill out!';
    }
  }

  if (/(\?+)\s*$/.test(message)) {
    return 'Sure.';
  }

  return 'Whatever.';
};

Esta solución resuelve el problema, pero se puede mejorar, sobretodo desde el punto de legibilidad. Siempre es importante recordad que el código que escribimos no será solamente interpretado por la computadora, sino que será leído por otros programadores y por nosotros mismos en el futuro. Lo más probable es que si en seis meses miro otra vez este código, no entienda mucho del mismo, porque en general no trabajo muy a menudo con expresiones regulares.

Solución final

export const hey = (message) => {
  if (isYelling(message)) {
    return isAsking(message) ? 'Calm down, I know what I\'m doing!' : 'Whoa, chill out!';
  }

  if (isAsking(message)) {
    return 'Sure.';
  }

  return isNotSayingNothing(message) ? 'Fine. Be that way!' : 'Whatever.';
};

const isNotSayingNothing = message => /^\s*$/.test(message);
const isAsking = message => /\?+\s*$/.test(message);
const isYelling = message => containsUppercase(message) && !containsLowercase(message);
const containsUppercase = message => /[A-Z]/.test(message);
const containsLowercase = message => /[a-z]/.test(message);

Ahora es muy diferente. Incluso si no recordamos muy bien como funcionan las expresiones regulares, con solo leer el nombre de las funciones podemos tener una idea de lo que estamos intentando modelar.

La milla extra

Hasta ahora estamos modelando oraciones que contienen palabras del idioma inglés. ¿Pero qué sucede cuando tenemos que tener en cuenta caracteres no latinos? Bueno ese es un problema un poco complicado, sobretodo porque JavaScript no soporta totalmente el standard Unicode. Aquí les dejo este artículo: What every JavaScript developer should know about unicode, que pienso les resultará muy interesante.

Referencias


Jorge Glez

Escrito por Jorge Glez. Un ser humano con defectos y virtudes que intenta ser mejor cada día. Twitter