2010-12-22 33 views
6

Por ejemplo, las palabras "pila", quiero obtener una matriz como:¿Cuál es la mejor manera de dividir una cadena para obtener todas las subcadenas de Ruby?

['s', 'st', 'sta', ... 'stack', 't', 'ta', ... , 'c', 'ck', 'k'] 

Hice esto por dicho código:

def split_word(str) 
    result = [] 
    chas = str.split("") 
    len = chas.size 
    (0..len-1).each do |i| 
    (i..len-1).each do |j| 
     result.push(chas[i..j].join) 
    end 
    end 
    result.uniq 
end 

¿Hay manera mejor y más limpia de hacer eso? Gracias.

+0

¿Con o sin duplicados? –

+0

sin duplicados, gracias! –

+0

consejos generales: use mejor el mapa (funcional) en lugar de seguir el patrón "init empty + iterate + push + return" (imperativo) – tokland

Respuesta

11
def split_word s 
    (0..s.length).inject([]){|ai,i| 
    (1..s.length - i).inject(ai){|aj,j| 
     aj << s[i,j] 
    } 
    }.uniq 
end 

y también se puede considerar el uso de Set en lugar de la matriz para el resultado.

PD: Aquí hay otra idea, basada en la matriz de producto:

def split_word s 
    indices = (0...s.length).to_a 
    indices.product(indices).reject{|i,j| i > j}.map{|i,j| s[i..j]}.uniq 
end 
+0

¡qué buena solución! –

+0

segunda solución fija después de una sugerencia del usuario @steel –

2

No lo creo.

Aquí está mi intento de versión:

def split_word(str) 
    length = str.length - 1 
    [].tap do |result| 
    0.upto(length) do |i| 
     length.downto(i) do |j| 
     substring = str[i..j] 
     result << substring unless result.include?(substring) 
     end 
    end 
    end 
end 
+0

¡Bien! Funciona y se ve más limpio. –

3

que escribiría:

def split_word(s) 
    0.upto(s.length - 1).flat_map do |start| 
    1.upto(s.length - start).map do |length| 
     s[start, length] 
    end 
    end.uniq 
end 

groups = split_word("stack") 
# ["s", "st", "sta", "stac", "stack", "t", "ta", "tac", "tack", "a", "ac", "ack", "c", "ck", "k"] 

Por lo general es más clara y más compacto para usar map (funcional) en lugar de la patrón init empty + each + append + return (imperativo).

+0

Sí, está en tokland.Prefiero el patrón funcional que el patrón imperativo también. ¡Gracias por tu sugerencia! ¿Y qué patrón crees que tiene el mejor rendimiento? –

+0

@ pake007: bueno, dije que generalmente es más rápido, pero aquí no estaba tan seguro debido a la aplanación y la falta de pereza de las matrices de Ruby. De todos modos, no te preocupes por el rendimiento, la diferencia es probablemente mínima. – tokland

0

Mucho más tarde, pero esto es lo que obtuve al formatear el código un poco.

def substrings(string) 
    siz = string.length 
    answer = [] 

    (0..siz-1).each do |n| 
    (n..siz-1).each do |i| 
     answer << string[n..i] 
    end 
    end 
    answer 
end 
2
def substrings(str) 
    output = [] 
    (0...str.length).each do |i| 
    (i...str.length).each do |j| 
     output << str[i..j] 
    end 
    end 
    output 
end 

esto es sólo una versión limpia de su método y funciona con menos pasos =)

2
def substrings(str) 
    (0...str.length).map do |i| 
    (i...str.length).each { |j| str[i..j]} 
    end 
end 

Sólo otra manera de hacerlo, que lee un poco más claro para mí.

Cuestiones relacionadas