2016-03-16 7 views
1

Попытка определить, как считать вхождения char является string. Я должен был быть сохранен в списке [char,count].Количество вхождений символов в строке Haskell

countChars :: String -> [(Char, Int)]

Я новичок и обучение Haskell поэтому любая помощь очень ценится.

+0

Я думаю, вы должны решить это, используя [рекурсию] (http://learnyouahaskell.com/recursion)? – mb21

+0

@ mb21 Я так предполагаю. Я только начал изучать haskell, и это бросает меня на такой цикл, потому что я использовал только объектно-ориентированные языки, а не функциональные. –

+1

@ ZackHerbert, вы можете показать, что вы пробовали, и где вы застряли? – utdemir

ответ

3

, чтобы дать вам вкус

> (map (head &&& length) . group . sort) "asdfasdfaeadf" 
[('a',4),('d',3),('e',1),('f',3),('s',2)] 

после сгустка или импорта.

Вы можете легко определить вашу голову & & & длина, если этот синтаксис незнакомец.

> head_and_length x = (head x, length x) 

группа может быть записана рекурсивно

group [] = [] 
group (x:xs) = (x:ys) : group zs 
    where (ys,zs) = (takeWhile (==x) xs, dropWhile (==x) xs) 

взять столько элементов, соответствующих как можно и рекурсивно применить на остальных элементах, пока ничего не осталось. Обратите внимание, что это определение требует, чтобы одни и те же элементы были смежными, поэтому требуется сортировка.

Написание комплексной функции не рекомендуется, но лучше реализовать небольшие функции отдельно (и, возможно, использовать существующие функции), которые могут быть независимо протестированы и создать их состав как окончательное решение.

+3

Это несколько ужасает даже для промежуточного программиста Haskell (даже если они знают, где ' &&& '); это никоим образом не полезно для кого-то, только начинающего. – chepner

+0

Я не согласен, придумывая это решение требует опыта, но не для понимания.Посмотрев на результат, вы увидите, что отображаемая функция создает пару с символом char и int, и понятно, откуда они. Точно так же, чтобы подсчитывать каждое событие, сортировка и группировка должны быть интуитивными и более легкими, чем рекурсивное решение. – karakfa

+0

Мне действительно нравится это решение и считаю его доступным для чтения, но, возможно, это слишком много для новичка. – chi

1

Это не будет полезно, но это будет быстро. Хитрость заключается в том, чтобы использовать IntMap, очень эффективное представление карт с ключами Int, чтобы сохранить, сколько из каждого символа было замечено. Мы используем символов для ключей, поэтому начинаем с создания типа CharMap, обертывающего IntMap и записывающего некоторые функции, которые завершают их эквиваленты IntMap.

import Data.Foldable (Foldable, foldl') 
import Control.Applicative ((<|>)) 
import qualified Data.IntMap.Strict as IM 
import Data.IntMap (IntMap) 

-- Strict version of Data.Bifunctor.first 
first :: (a -> a') -> (a, b) -> (a', b) 
first f (a, b) = (f a, b) 

newtype CharMap a = CharMap (IntMap a) 

emptyCM :: CharMap a 
emptyCM = CharMap (IM.empty) 

toAssocAsc :: CharMap a -> [(Char, a)] 
toAssocAsc (CharMap m) = map (first toEnum) (IM.toAscList m) 

alter :: (Maybe a -> Maybe a) -> Char -> CharMap a -> CharMap a 
alter f c (CharMap m) = CharMap $ IM.alter f (fromEnum c) m 

Это было немного раздражающим, но скорее механическим. Вот главное событие. Мы бросаем символы в CharMap, сохраняя их сопоставленными с их подсчетами, а затем преобразуем карту в список ассоциаций, когда мы закончим.

countCharacterOccurrences :: 
    Foldable t => t Char -> [(Char, Int)] 
countCharacterOccurrences = toAssocAsc . foldl' go emptyCM 
    where 
    go m c = alter (\curr -> fmap (+1) curr <|> Just 1) c m 
Смежные вопросы