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/ /.
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)
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
\1no son compatibles. Para extraer texto alrededor de un punto de referencia, usa lookaround (más abajo).
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)
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
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.
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.
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
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" }
Define percentages, que devuelve cada número al que le sigue inmediatamente un %.
"up 5% then down 12%" percentages .
! => { "5" "12" }
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
Regístrate en Exercism para aprender y dominar Factor con 47 conceptos163 ejercicios y mentoría humana real, todo gratis.