¿Cuál es la forma más rápida de comprobar si una cadena coincide con una expresión regular en Ruby?
Mi problema es que tengo que "egrep" a través de una enorme lista de cadenas para encontrar cuáles son las que coinciden con una expresión regular que se proporciona en tiempo de ejecución. Solo me importa si la cadena coincide con la expresión regular, no dónde coincide ni cuál es el contenido de los grupos coincidentes. Espero que esta suposición se pueda utilizar para reducir la cantidad de tiempo que mi código pasa haciendo coincidir expresiones regulares.
Cargo la expresión regular con
pattern = Regexp.new(ptx).freeze
He descubierto que string =~ pattern
es un poco más rápido que string.match(pattern)
.
¿Hay otros trucos o atajos que puedan usarse para hacer esta prueba aún más rápida?
fuente
Respuestas:
A partir de Ruby 2.4.0, puede usar
RegExp#match?
:Regexp#match?
se enumera explícitamente como una mejora de rendimiento en las notas de la versión para 2.4.0 , ya que evita las asignaciones de objetos realizadas por otros métodos comoRegexp#match
y=~
:fuente
Regexp#match?
hecho, es al menos un 50% más rápido que las otras alternativas.Este es un punto de referencia simple:
Entonces
=~
es más rápido, pero depende de lo que desee tener como valor devuelto. Si solo desea verificar si el texto contiene una expresión regular o no usar=~
fuente
=~
es más rápido quematch
, con un aumento de rendimiento menos dramático cuando se opera en expresiones regulares más grandes. Lo que me pregunto es si hay alguna forma extraña de hacer que esta verificación sea aún más rápida, tal vez explotando algún método extraño en Regexp o alguna construcción extraña.!("test123" !~ /1/)
?"test123" =~ /1/
/1/.match?("test123")
es más rápido que"test123" =~ /1/
si es solo para verificar si el texto contiene una expresión regular o no.Este es el punto de referencia que he realizado después de encontrar algunos artículos en la red.
Con 2.4.0 el ganador es
re.match?(str)
(como sugiere @ wiktor-stribiżew), en versiones anteriores,re =~ str
parece ser el más rápido, aunquestr =~ re
es casi tan rápido.Resultados RM 1.9.3-o551:
Resultados RM 2.1.5:
Resultados MRI 2.3.3 (parece que hay una regresión en la coincidencia de expresiones regulares):
Resultados MRI 2.4.0:
fuente
/a+b/ =~ str
ystr =~ /a+b/
. Es válido incluso cuando se iteran a través de funciones y lo veo lo suficientemente válido como para considerarlo mejor que almacenar y congelar expresiones regulares en una variable. Probé mi script con ruby 1.9.3p547, ruby 2.0.0p481 y ruby 2.1.4p265. Es posible que estas mejoras se hayan realizado en parches posteriores, pero todavía no tengo planes de probarlo con versiones / parches anteriores.!(re !~ str)
podría ser más rápido, pero no lo es.¿Qué pasa con
re === str
(comparación de casos)?Dado que se evalúa como verdadero o falso y no tiene necesidad de almacenar coincidencias, devolver el índice de coincidencia y esas cosas, me pregunto si sería una forma aún más rápida de hacer coincidencias que
=~
.Ok, probé esto.
=~
es aún más rápido, incluso si tiene varios grupos de captura, sin embargo, es más rápido que las otras opciones.Por cierto, ¿de qué sirve
freeze
? No pude medir ningún aumento de rendimiento de eso.fuente
freeze
no aparecerán en los resultados porque ocurre antes de los bucles de referencia y actúa sobre el patrón en sí.Dependiendo de lo complicada que sea su expresión regular, posiblemente podría usar un simple corte de cadena. No estoy seguro de la practicidad de esto para su aplicación o si realmente ofrecería alguna mejora de velocidad.
fuente
Los motores de expresiones regulares varían en la forma en que implementan las búsquedas, pero, en general, anclan sus patrones de velocidad y evitan coincidencias codiciosas, especialmente cuando se buscan cadenas largas.
Lo mejor que puede hacer, hasta que esté familiarizado con el funcionamiento de un motor en particular, es hacer evaluaciones comparativas y agregar / eliminar anclajes, intentar limitar las búsquedas, usar comodines frente a coincidencias explícitas, etc.
La gema frutal es muy útil para evaluar rápidamente las cosas, porque es inteligente. El código Benchmark incorporado de Ruby también es útil, aunque puede escribir pruebas que lo engañen si no tiene cuidado.
He usado ambos en muchas respuestas aquí en Stack Overflow, por lo que puede buscar entre mis respuestas y verá muchos pequeños trucos y resultados para darle ideas sobre cómo escribir código más rápido.
Lo más importante para recordar es que es malo optimizar prematuramente su código antes de saber dónde ocurren las ralentizaciones.
fuente
Para completar las respuestas de Wiktor Stribiżew y Dougui , diría eso
/regex/.match?("string")
tan rápido como"string".match?(/regex/)
.Ruby 2.4.0 (10000000 ~ 2 segundos)
Ruby 2.6.2 (100 000 000 ~ 20 segundos)
Nota: los tiempos varían, a veces
/regex/.match?("string")
es más rápido y"string".match?(/regex/)
, a veces , las diferencias pueden deberse solo a la actividad de la máquina.fuente