Encontrar si una oración contiene una frase específica en Ruby

Ahora mismo estoy viendo si una oración contiene una palabra específica al dividir la oración en una matriz y luego hacer una inclusión para ver si contiene la palabra. Algo como:

"This is my awesome sentence.".split(" ").include?('awesome') 

Pero me pregunto cuál es la forma más rápida de hacer esto con una frase. Como si quisiera ver si la frase “Esta es mi frase impresionante”. contiene la frase “mi oración impresionante”. Estoy raspando oraciones y comparando una gran cantidad de frases, por lo que la velocidad es algo importante.

Aquí hay algunas variaciones:

 require 'benchmark' lorem = ('Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut' # !> unused literal ignored 'enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in' # !> unused literal ignored 'reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident,' # !> unused literal ignored 'sunt in culpa qui officia deserunt mollit anim id est laborum.' * 10) << ' foo' lorem.split.include?('foo') # => true lorem['foo'] # => "foo" lorem.include?('foo') # => true lorem[/foo/] # => "foo" lorem[/fo{2}/] # => "foo" lorem[/foo$/] # => "foo" lorem[/fo{2}$/] # => "foo" lorem[/fo{2}\Z/] # => "foo" /foo/.match(lorem)[-1] # => "foo" /foo$/.match(lorem)[-1] # => "foo" /foo/ =~ lorem # => 621 n = 500_000 puts RUBY_VERSION puts "n=#{ n }" Benchmark.bm(25) do |x| x.report("array search:") { n.times { lorem.split.include?('foo') } } x.report("literal search:") { n.times { lorem['foo'] } } x.report("string include?:") { n.times { lorem.include?('foo') } } x.report("regex:") { n.times { lorem[/foo/] } } x.report("wildcard regex:") { n.times { lorem[/fo{2}/] } } x.report("anchored regex:") { n.times { lorem[/foo$/] } } x.report("anchored wildcard regex:") { n.times { lorem[/fo{2}$/] } } x.report("anchored wildcard regex2:") { n.times { lorem[/fo{2}\Z/] } } x.report("/regex/.match") { n.times { /foo/.match(lorem)[-1] } } x.report("/regex$/.match") { n.times { /foo$/.match(lorem)[-1] } } x.report("/regex/ =~") { n.times { /foo/ =~ lorem } } x.report("/regex$/ =~") { n.times { /foo$/ =~ lorem } } x.report("/regex\Z/ =~") { n.times { /foo\Z/ =~ lorem } } end 

Y los resultados de Ruby 1.9.3:

 1.9.3
 n = 500000
                                 sistema de usuario total real
 búsqueda de matriz: 12.960000 0.010000 12.970000 (12.978311)
 búsqueda literal: 0.800000 0.000000 0.800000 (0.807110)
 la cadena incluye ?: 0.760000 0.000000 0.760000 (0.758918)
 expresiones regulares: 0.660000 0.000000 0.660000 (0.657608)
 expresión regular de comodín: 0.660000 0.000000 0.660000 (0.660296)
 regex anclado: 0.660000 0.000000 0.660000 (0.664025)
 comodín anclado regex: 0.660000 0.000000 0.660000 (0.664897)
 comodín anclado regex2: 0.320000 0.000000 0.320000 (0.328876)
 /regex/.match 1.430000 0.000000 1.430000 (1.424602)
 /regex$/.match 1.430000 0.000000 1.430000 (1.434538)
 / regex / = ~ 0.530000 0.000000 0.530000 (0.538128)
 / regex $ / = ~ 0.540000 0.000000 0.540000 (0.536318)
 / regexZ / = ~ 0.210000 0.000000 0.210000 (0.214547)

Y 1.8.7:

 1.8.7
 n = 500000
                                sistema de usuario total real
 búsqueda de matriz: 21.250000 0.000000 21.250000 (21.296039)
 búsqueda literal: 0.660000 0.000000 0.660000 (0.660102)
 la cadena incluye ?: 0.610000 0.000000 0.610000 (0.612433)
 expresiones regulares: 0.950000 0.000000 0.950000 (0.946308)
 expresión regular de comodín: 2.840000 0.000000 2.840000 (2.850198)
 regex anclado: 0.950000 0.000000 0.950000 (0.951270)
 comodín anclado regex: 2.870000 0.010000 2.880000 (2.874209)
 comodín anclado regex2: 2.870000 0.000000 2.870000 (2.868291)
 /regex/.match 1.470000 0.000000 1.470000 (1.479383)
 /regex$/.match 1.480000 0.000000 1.480000 (1.498106)
 / regex / = ~ 0.680000 0.000000 0.680000 (0.677444)
 / regex $ / = ~ 0.700000 0.000000 0.700000 (0.704486)
 / regexZ / = ~ 0.700000 0.000000 0.700000 (0.701943)

Entonces, a partir de los resultados, usar una búsqueda de cadena fija como 'foobar'['foo'] es más lento que usar una expresión regular 'foobar'[/foo/] , que es más lento que el equivalente 'foobar' =~ /foo/ .

La solución original de los OP sufre mucho porque atraviesa la cadena dos veces: una vez para dividirla en palabras individuales y una segunda vez para iterar la matriz en busca de la palabra de destino real. Su rendimiento se degradará peor a medida que aumenta el tamaño de la cadena.


Edición: Una cosa que me parece interesante sobre el rendimiento de Ruby, es que una expresión regular anclada es un poco más lenta que la expresión regular no anclada. En Perl, lo contrario era cierto cuando ejecuté este tipo de referencia por primera vez, hace varios años.


Aquí hay una versión actualizada usando Fruity. Las diversas expresiones devuelven resultados diferentes. Se puede utilizar cualquiera si desea ver si existe la cadena de destino. Si desea ver si el valor se encuentra al final de la cadena, como estos están probando, o para obtener la ubicación del objective, entonces algunos son definitivamente más rápidos que otros, así que elija en consecuencia.

 require 'fruity' TARGET_STR = (' ' * 100) + ' foo' TARGET_STR['foo'] # => "foo" TARGET_STR[/foo/] # => "foo" TARGET_STR[/fo{2}/] # => "foo" TARGET_STR[/foo$/] # => "foo" TARGET_STR[/fo{2}$/] # => "foo" TARGET_STR[/fo{2}\Z/] # => "foo" TARGET_STR[/fo{2}\z/] # => "foo" TARGET_STR[/foo\Z/] # => "foo" TARGET_STR[/foo\z/] # => "foo" /foo/.match(TARGET_STR)[-1] # => "foo" /foo$/.match(TARGET_STR)[-1] # => "foo" /foo/ =~ TARGET_STR # => 101 /foo$/ =~ TARGET_STR # => 101 /foo\Z/ =~ TARGET_STR # => 101 TARGET_STR.include?('foo') # => true TARGET_STR.index('foo') # => 101 TARGET_STR.rindex('foo') # => 101 puts RUBY_VERSION puts "TARGET_STR.length = #{ TARGET_STR.length }" puts puts 'compare fixed string vs. unanchored regex' compare do fixed_str { TARGET_STR['foo'] } unanchored_regex { TARGET_STR[/foo/] } end puts puts 'compare /foo/ to /fo{2}/' compare do unanchored_regex { TARGET_STR[/foo/] } unanchored_regex2 { TARGET_STR[/fo{2}/] } end puts puts 'compare unanchored vs. anchored regex' # !> assigned but unused variable - delay compare do unanchored_regex { TARGET_STR[/foo/] } anchored_regex_dollar { TARGET_STR[/foo$/] } anchored_regex_Z { TARGET_STR[/foo\Z/] } anchored_regex_z { TARGET_STR[/foo\z/] } end puts puts 'compare /foo/, match and =~' compare do unanchored_regex { TARGET_STR[/foo/] } unanchored_match { /foo/.match(TARGET_STR)[-1] } unanchored_eq_match { /foo/ =~ TARGET_STR } end puts puts 'compare fixed, unanchored, Z, include?, index and rindex' compare do fixed_str { TARGET_STR['foo'] } unanchored_regex { TARGET_STR[/foo/] } anchored_regex_Z { TARGET_STR[/foo\Z/] } include_eh { TARGET_STR.include?('foo') } _index { TARGET_STR.index('foo') } _rindex { TARGET_STR.rindex('foo') } end 

Lo que resulta en:

 # >> 2.2.3 # >> TARGET_STR.length = 104 # >> # >> compare fixed string vs. unanchored regex # >> Running each test 8192 times. Test will take about 1 second. # >> fixed_str is faster than unanchored_regex by 2x ± 0.1 # >> # >> compare /foo/ to /fo{2}/ # >> Running each test 8192 times. Test will take about 1 second. # >> unanchored_regex2 is similar to unanchored_regex # >> # >> compare unanchored vs. anchored regex # >> Running each test 8192 times. Test will take about 1 second. # >> anchored_regex_z is similar to anchored_regex_Z # >> anchored_regex_Z is faster than unanchored_regex by 19.999999999999996% ± 10.0% # >> unanchored_regex is similar to anchored_regex_dollar # >> # >> compare /foo/, match and =~ # >> Running each test 8192 times. Test will take about 1 second. # >> unanchored_eq_match is faster than unanchored_regex by 2x ± 0.1 (results differ: 101 vs foo) # >> unanchored_regex is faster than unanchored_match by 3x ± 0.1 # >> # >> compare fixed, unanchored, Z, include?, index and rindex # >> Running each test 32768 times. Test will take about 3 seconds. # >> _rindex is similar to include_eh (results differ: 101 vs true) # >> include_eh is faster than _index by 10.000000000000009% ± 10.0% (results differ: true vs 101) # >> _index is faster than fixed_str by 19.999999999999996% ± 10.0% (results differ: 101 vs foo) # >> fixed_str is faster than anchored_regex_Z by 39.99999999999999% ± 10.0% # >> anchored_regex_Z is similar to unanchored_regex 

Modificar el tamaño de la cadena revela buenas cosas que debe saber.

Cambiando a 1,000 caracteres:

 # >> 2.2.3 # >> TARGET_STR.length = 1004 # >> # >> compare fixed string vs. unanchored regex # >> Running each test 4096 times. Test will take about 1 second. # >> fixed_str is faster than unanchored_regex by 50.0% ± 10.0% # >> # >> compare /foo/ to /fo{2}/ # >> Running each test 2048 times. Test will take about 1 second. # >> unanchored_regex2 is similar to unanchored_regex # >> # >> compare unanchored vs. anchored regex # >> Running each test 8192 times. Test will take about 1 second. # >> anchored_regex_z is faster than anchored_regex_Z by 10.000000000000009% ± 10.0% # >> anchored_regex_Z is faster than unanchored_regex by 3x ± 0.1 # >> unanchored_regex is similar to anchored_regex_dollar # >> # >> compare /foo/, match and =~ # >> Running each test 4096 times. Test will take about 1 second. # >> unanchored_eq_match is similar to unanchored_regex (results differ: 1001 vs foo) # >> unanchored_regex is faster than unanchored_match by 2x ± 0.1 # >> # >> compare fixed, unanchored, Z, include?, index and rindex # >> Running each test 32768 times. Test will take about 4 seconds. # >> _rindex is faster than anchored_regex_Z by 2x ± 1.0 (results differ: 1001 vs foo) # >> anchored_regex_Z is faster than include_eh by 2x ± 0.1 (results differ: foo vs true) # >> include_eh is faster than fixed_str by 10.000000000000009% ± 10.0% (results differ: true vs foo) # >> fixed_str is similar to _index (results differ: foo vs 1001) # >> _index is similar to unanchored_regex (results differ: 1001 vs foo) 

Chocándolo a 10,000:

 # >> 2.2.3 # >> TARGET_STR.length = 10004 # >> # >> compare fixed string vs. unanchored regex # >> Running each test 512 times. Test will take about 1 second. # >> fixed_str is faster than unanchored_regex by 39.99999999999999% ± 10.0% # >> # >> compare /foo/ to /fo{2}/ # >> Running each test 256 times. Test will take about 1 second. # >> unanchored_regex2 is similar to unanchored_regex # >> # >> compare unanchored vs. anchored regex # >> Running each test 8192 times. Test will take about 3 seconds. # >> anchored_regex_z is similar to anchored_regex_Z # >> anchored_regex_Z is faster than unanchored_regex by 21x ± 1.0 # >> unanchored_regex is similar to anchored_regex_dollar # >> # >> compare /foo/, match and =~ # >> Running each test 256 times. Test will take about 1 second. # >> unanchored_eq_match is similar to unanchored_regex (results differ: 10001 vs foo) # >> unanchored_regex is faster than unanchored_match by 10.000000000000009% ± 10.0% # >> # >> compare fixed, unanchored, Z, include?, index and rindex # >> Running each test 32768 times. Test will take about 18 seconds. # >> _rindex is faster than anchored_regex_Z by 2x ± 0.1 (results differ: 10001 vs foo) # >> anchored_regex_Z is faster than include_eh by 15x ± 1.0 (results differ: foo vs true) # >> include_eh is similar to _index (results differ: true vs 10001) # >> _index is similar to fixed_str (results differ: 10001 vs foo) # >> fixed_str is faster than unanchored_regex by 39.99999999999999% ± 10.0% 

Puede comprobar fácilmente si una cadena contiene otra cadena con corchetes como:

 irb(main):084:0> "This is my awesome sentence."["my awesome sentence"] => "my awesome sentence" irb(main):085:0> "This is my awesome sentence."["cookies for breakfast?"] => nil 

devolverá la cadena secundaria si la encuentra o nil si no la encuentra. Debería ser muy rápido.

Aquí hay una no-respuesta que muestra el punto de referencia para el código de @TheTinMan para Ruby 1.9.2 en OS X. Observe la diferencia en el rendimiento relativo, específicamente las mejoras en las pruebas segunda y tercera.

                                user     system      total        real array search:              7.960000   0.000000   7.960000 (  7.962338) literal search:            0.450000   0.010000   0.460000 (  0.445905) string include?:           0.400000   0.000000   0.400000 (  0.400932) regex:                     0.510000   0.000000   0.510000 (  0.512635) wildcard regex:            0.520000   0.000000   0.520000 (  0.514800) anchored regex:            0.510000   0.000000   0.510000 (  0.513328) anchored wildcard regex:   0.520000   0.000000   0.520000 (  0.517759) /regex/.match              0.940000   0.000000   0.940000 (  0.943471) /regex$/.match             0.940000   0.000000   0.940000 (  0.936782) /regex/ =~                 0.440000   0.000000   0.440000 (  0.446921) /regex$/ =~                0.450000   0.000000   0.450000 (  0.447904) 

Corrí estos resultados con Benchmark.bmbm , pero los resultados no difieren entre la ronda de ensayo y los tiempos reales, que se muestran arriba.

Si no está familiarizado con las expresiones regulares, creo que pueden resolver su problema aquí:

http://www.regular-expressions.info/ruby.html

Básicamente, creará un objeto de expresión regular en busca de “asombroso” (lo más probable es que no tenga en cuenta las mayúsculas) y luego puede hacerlo

 /regex/.match(string) 

Para devolver los datos del partido. Si desea devolver el índice en el que se encuentra el carácter, puede hacer esto:

 match = "This is my awesome sentence." =~ /awesome/ puts match #This will return the index of the first letter, so the first a in awesome 

Había leído el artículo para más detalles, ya que lo explica mejor que yo. Si no quieres entenderlo tanto y solo quieres usarlo, te recomiendo esto:

http://www.ruby-doc.org/core/classes/Regexp.html