2010-12-22 8 views
6

Na przykład słowa "stos", chcę uzyskać tablicę jak:Jaki jest najlepszy sposób na podzielenie łańcucha, aby uzyskać wszystkie podciągi przez Ruby?

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

Robiłam to przez takiego kodu:

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 

Czy istnieje lepszy i czysty sposób to zrobić? Dzięki.

+0

Z kopiami lub bez duplikatów? –

+0

bez duplikatów, dzięki! –

+0

porada ogólna: lepiej używać mapy (funkcjonalnie) zamiast podążać za wzorcem "init empty + iterate + push + return" (imperative) – tokland

Odpowiedz

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 

Można również rozważyć użycie Set zamiast Array dla wyniku.

PS: Oto kolejny pomysł, oparty na produkcie tablicy:

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

co za fajne rozwiązanie! –

+0

naprawiono drugie rozwiązanie po sugestii użytkownika @samel –

2

Nie sądzę.

Oto moja próba wersja:

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

Dobrze! Działa i wygląda czystsze. –

3

bym napisać:

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"] 

To zwykle bardziej przejrzyste i bardziej kompaktowy używać map (funkcjonalne) zamiast wzór init pusty + każdy + dołącz + powróć (imperatyw).

+0

Tak, masz rację.Wolę wzór funkcjonalny niż wzór imperatywny. Dzięki za Twoją sugestię! A jaki wzór ma lepsze wyniki? –

+0

@ pake007: cóż, powiedziałem, że zwykle jest to szybsze, ale tutaj nie jestem tak pewny z powodu spłaszczenia i braku lenistwa tablic Rubiego. W każdym razie, nie poci się za wydajność, różnica jest prawdopodobnie minimalna. – tokland

0

Sposób później, ale to jest to, co otrzymałem od ponownego formatowania twojego kodu.

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 

to tylko oczyścić wersję swojej metody i działa z mniejszą ilością kroków =)

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

Jeszcze jeden sposób na to, że brzmi trochę jaśniejsze do mnie.

Powiązane problemy