2011-08-18 13 views
17

Jak mogę policzyć częstotliwość znaków w ciągu znaków, a następnie wypisać je w formie tabeli?Jak znaleźć częstotliwość znaków w ciągu znaków w Haskell?

Na przykład, jeśli wejście słowo „szczęśliwy” wynik byłby

h 1 
a 1 
p 2 
y 1 

jeżeli mogłoby to być uporządkowane w kolejności ASCII też, że byłoby genialne.

Wiem, że muszę korzystać z funkcji liczenia, wszelkie inne wskazówki byłyby mile widziane.

EDYCJA: Wszystkie odpowiedzi są genialne, ale jestem tak początkującym w Haskell, że nie rozumiem, co robią.

Odpowiedz

9

Jest chyba coś krótsze, ale to działa:

Prelude> import Data.List 
Prelude Data.List> map (\x -> (head x, length x)) $ group $ sort "happy" 
[('h',1),('a',1),('p',2),('y',1)] 
+1

Musisz uporządkować wejście pierwsze obejmuje przypadki, jak '' „pappy” gdzie wystąpienia 'p' nie sąsiadują. – hammar

+0

Dzięki, naprawione. :-) –

+2

i zauważ, że '(\ x -> (head x, length x)) == head &&& length', gdzie' (&&&) 'pochodzi od' Control.Arrow'. – Conal

39

Najprostszym rozwiązaniem jest użycie Data.Map do przechowywania pośredniego mapowania z charakterem częstotliwości. Możesz łatwo skonstruować liczbę za pomocą fromListWith. Ponieważ sortowane jest Data.Map, dostajesz je w porządku ASCII za darmo.

λ> :m + Data.Map 
λ> let input = "happy" 
λ> toList $ fromListWith (+) [(c, 1) | c <- input] 
[('a',1),('h',1),('p',2),('y',1)] 

Więc co tu się dzieje?

Chodzi o to, aby zbudować Data.Map (mapę drzewa), używając znaków jako klawiszy i częstotliwości jako wartości.

Najpierw pobieramy łańcuch wejściowy i robimy krotki każdego znaku, podając 1, aby wskazać jedno wystąpienie.

λ> [(c, 1) | c <- input] 
[('h',1),('a',1),('p',1),('p',1),('y',1)] 

Następnie używamy fromListWith budować posortowaną mapę z tych par klucz-wartość poprzez wielokrotne wstawianie każdą parę klucz-wartość w mapie. Dajemy mu także funkcję, która będzie używana, gdy klucz był już na mapie. W naszym przypadku używamy (+), więc gdy postać jest widziana wiele razy, dodajemy liczbę do istniejącej sumy.

Na koniec zamieniamy mapę z powrotem na listę krotek klucz-wartość za pomocą toList.

+0

Myślę, że jestem głupi, ale czy to program? Jestem takim noobem w haskell, przepraszam, jeśli to głupie pytanie. – Hagrid123

+0

@ Hagrid123: Przykłady pochodzą z sesji GHCi (tłumacza), która jest nieco inna niż w źródłowym pliku Haskella. Na przykład 'let' jest używane dla powiązań najwyższego poziomu, a': m' można użyć do importu modułu. – hammar

+2

Dla rekordu cechą znaku zachęty GHCi jest znak '>. Kiedy po raz pierwszy uruchomisz ghci, prawdopodobnie zobaczysz 'Prelude>'; zauważ, że moduły w zakresie są wymienione w monicie. GHC Hammarda wydaje się być wypluwany. –

4

func xs = map (\a -> (head a, length a)) $ group $ sort xs

+0

'groupBy (\ x y -> x == y)' jest takie samo jak 'grupa' – newacct

+0

Tak, zdałem sobie sprawę, że w momencie, kiedy to opublikowałem. :) – Marii

0

będę scetch rozwiązanie krok po kroku. Krótsze rozwiązanie jest możliwe przy użyciu standardowych funkcji.

Chcesz posortowaną rezultatu, dlatego

result = sort cs 
    where 

cs byłaby lista krotek, gdzie pierwszym elementem jest charakter i drugi element jest kilka razy się pojawia.

 cs = counts "happy" 
     counts [] = [] 
     counts (c:cs) = (c, length otherc + 1) : counts nonc where 
      (otherc, nonc) = partition (c==) cs 

To wszystko.

Co ciekawe, liczy się na dowolnej liście pozycji, które obsługują operatora ==.

0
import Data.Array (Ix, accumArray, assocs) 

eltDist :: (Bounded a, Ix a, Eq b, Num b) => [a] -> [(a, b)] 
eltDist str = filter ((/=0) . snd) $ 
    assocs (accumArray (+) 0 (minBound, maxBound) [(i, 1) | i <- str]) 

"minBound" i "maxBound" będą zależeć od zakresu typu wywnioskowanego dla i. Dla Char będzie to 0 - 1 114 114, co jest ekstrawaganckie, ale nie niemożliwe. Byłoby to szczególnie wygodne, gdybyś liczył znaki Unicode. Jeśli interesują Cię tylko ciągi ASCII, to (0, 255) by to zrobiło. Zaletą tablic jest to, że mogą być indeksowane przez dowolny typ, który można odwzorować na liczbę całkowitą. Zobacz Ix.

Assocs ściąga indeksy i zlicza z tablicy na listę par i filtruje niezajęte.

3

Posługuj się rozumieniem listy, nie musisz importować ani sortować.

[ (x,c) | x<-['A'..'z'], let c = (length.filter (==x)) "happy", c>0 ] 

Wynik:

[('a',1),('h',1),('p',2),('y',1)] 

powyżej jest filtrowana i przepisana (charakter wyłącznie count> 0) od:

[(x,(length.filter (==x)) "happy") | x<-['A'..'z']] 

Objaśnienie:

  • Zrób listę wszystkich znaków pasujących do danej postaci (A..z).
  • Dla każdego znaku, liczyć do tej listy (== długość)
  • Umieść ten licznik w krotce z charakterem
+0

Podoba mi się to! Jest to bardzo przydatne, gdy interesują Cię tylko częstotliwości niektórych znaków, a nie wszystkie znaki w łańcuchu wejściowym. Bardzo schludny. –

Powiązane problemy