Tracks
/
Factor
Factor
/
Ejercicios
/
Vigía del libro mayor
Vigía del libro mayor

Vigía del libro mayor

Ejercicio de aprendizaje

Introducción

Ya conoces lo básico: clases de caracteres como \d y [A-Z], cuantificadores como {4} y +, y las palabras matches?, all-matching-subseqs y re-replace. Este ejercicio agrega las partes de las expresiones regulares que describen estructura y contexto. Viven en el mismo vocabulario regexp, escrito con el mismo literal R/ /.

Anclas

Las anclas coinciden con una posición en lugar de con un carácter:

^    ! the start of the string
$    ! the end of the string
\b   ! a word boundary

Como $ significa el final del string, escribe \$ cuando quieras decir un signo de dólar literal.

Un límite de palabra (\b) es el punto donde un carácter de palabra se encuentra con uno que no lo es. Los caracteres de palabra son letras, dígitos y el guion bajo; todo lo demás (espacios, puntuación y los dos extremos del string) cuenta como límite. Así, \bcat\b coincide con cat solo cuando aparece por sí solo:

USING: regexp ;

"cat nap" R/ \bcat\b/ re-contains? .   ! => t
"scatter" R/ \bcat\b/ re-contains? .   ! => f   (cat sits inside a word)

Agrupación y alternancia

Los paréntesis ( ) agrupan parte de un patrón para que un cuantificador, o un | («esto o aquello»), se aplique a todo el grupo:

USING: regexp ;

"abab" R/ (ab)+/ matches? .                ! => t   (+ applies to "ab")
"dog"  R/ cat|dog/ matches? .              ! => t   (either word)
"$100.50" R/ \$\d+(\.\d{2})?/ matches? .   ! => t
"$100"    R/ \$\d+(\.\d{2})?/ matches? .   ! => t   (the group is optional)

El último patrón se lee «un signo de dólar, uno o más dígitos y luego un grupo opcional de un punto y dos dígitos».

Nota: en Factor, un grupo solo estructura el patrón. A diferencia de algunas herramientas de expresiones regulares, no puedes recuperar el texto que coincidió con un grupo: no hay extracción de grupos de captura, y las retrorreferencias como \1 no son compatibles. Para extraer texto alrededor de un punto de referencia, usa lookaround (más abajo).

Lookaround: coincidir por contexto, sin consumirlo

Un lookaround verifica qué viene justo antes o justo después del punto actual sin hacer que esos caracteres formen parte de la coincidencia. Es de «ancho cero»: lo que comprueba no se incluye en el resultado.

(?=...)    ! lookahead:           ... must follow
(?!...)    ! negative lookahead:  ... must NOT follow
(?<=...)   ! lookbehind:          ... must come before
(?<!...)   ! negative lookbehind: ... must NOT come before

El lookaround es selectivo: los mismos caracteres coinciden o no según lo que tengan al lado. Un \d+ simple captura todos los números; el lookbehind y el lookahead solo conservan los que tienen el vecino correcto:

USING: regexp ;

"3 for $10 or 10 for $30" R/ \d+/ all-matching-subseqs .
! => { "3" "10" "10" "30" }   (every number)

"3 for $10 or 10 for $30" R/ (?<=\$)\d+/ all-matching-subseqs .
! => { "10" "30" }            (only the prices — the bare 3 and 10 drop)

"buy 2 at 15% off, 4 at 30% off" R/ \d+(?=%)/ all-matching-subseqs .
! => { "15" "30" }            (only the discounts — the bare 2 and 4 drop)

Opciones

Agrega una letra después de la / de cierre para cambiar el comportamiento de todo el patrón. i lo hace insensible a mayúsculas y minúsculas:

USING: regexp ;

"REFUND"  R/ refund/i matches? .       ! => t
"Refund"  R/ refund/i matches? .       ! => t

Transformar cada coincidencia

all-matching-subseqs te entrega el texto que coincidió. map-matches va un paso más allá: ejecuta una quotation en cada coincidencia y recopila los resultados. La quotation recibe el índice start de la coincidencia, su índice end y el string completo; subseq convierte esos tres en el texto que coincidió, que luego transformas:

map-matches ( string regexp quot: ( start end string -- obj ) -- seq )
USING: math regexp sequences ;

"a1 b22 c333" R/ \d+/ [ subseq length ] map-matches .
! => { 1 2 3 }     (the length of each run of digits)

Su prima que solo produce efectos secundarios, each-match, ejecuta una quotation en cada coincidencia sin recopilar nada.

Instrucciones

Llevas la contabilidad de una pequeña empresa, y las transacciones de cada día llegan como líneas de texto desordenadas. Usarás expresiones regulares para validar montos, extraer cifras según el símbolo que las marca y marcar las líneas que necesitan una revisión más de cerca.

1. Valida un monto

Un monto bien formado es un $, uno o más dígitos y una parte de centavos opcional formada por un punto seguido de exactamente dos dígitos: $100 o $100.50, pero no $100.5. Define valid-amount?, que devuelve t cuando el string completo es un monto bien formado.

"$100.50" valid-amount? .   ! => t
"$100"    valid-amount? .   ! => t
"$100.5"  valid-amount? .   ! => f

2. Extrae las cifras en dólares

Define dollar-amounts, que devuelve cada número que sigue directamente a un $ en la línea (solo los dígitos, sin el $), en orden.

"spent $100 and $25 today" dollar-amounts .
! => { "100" "25" }

3. Extrae los porcentajes

Define percentages, que devuelve cada número al que le sigue inmediatamente un %.

"up 5% then down 12%" percentages .
! => { "5" "12" }

4. Marca una línea

Define flagged?, que devuelve t cuando la línea menciona refund o chargeback en cualquier combinación de mayúsculas y minúsculas.

"Issued a REFUND today" flagged? .   ! => t
"a normal sale"         flagged? .   ! => f
Editar en GitHub El enlace se abre en una ventana o una pestaña nuevas
Factor Exercism

¿Todo listo para empezar Vigía del libro mayor?

Regístrate en Exercism para aprender y dominar Factor con 47 conceptos163 ejercicios y mentoría humana real, todo gratis.