Показать список слов, повторяющихся в Haskell
Вопрос
Мне нужно иметь возможность написать функцию, которая показывает повторяющиеся слова из строки и возвращает список строк в порядке их появления и игнорирует небуквы.
например, при подсказке об объятиях
repetitions :: String -> [String]
repetitions > "My bag is is action packed packed."
output> ["is","packed"]
repetitions > "My name name name is Sean ."
output> ["name","name"]
repetitions > "Ade is into into technical drawing drawing ."
output> ["into","drawing"]
Решение
Чтобы разделить строку на слова, используйте команду words
функция (в Prelude).Чтобы исключить символы, не являющиеся словами, filter
с Data.Char.isAlphaNum
.Свяжите список вместе с его хвостом, чтобы получить соседние пары. (x, y)
.Сверните список, создав новый список, содержащий все x
где x
== y
.
Что-то вроде:
repetitions s = map fst . filter (uncurry (==)) . zip l $ tail l
where l = map (filter isAlphaNum) (words s)
Я не уверен, что это работает, но это должно дать вам примерное представление.
Другие советы
Я новичок в этом языке, поэтому мое решение может показаться уродливым в глазах ветерана Haskell, но в любом случае:
let repetitions x = concat (map tail (filter (\x -> (length x) > 1) (List.group (words (filter (\c -> (c >= 'a' && c <= 'z') || (c>='A' && c <= 'Z') || c==' ') x)))))
Эта часть удалит из строки все, кроме букв и пробелов. с:
filter (\c -> (c >= 'a' && c <= 'z') || (c>='A' && c <= 'Z') || c==' ') s
Этот разделит строку с к словам и группировать одни и те же слова в списки, возвращая список списков:
List.group (words s)
Когда эта часть удалит все списки, содержащие менее двух элементов:
filter (\x -> (length x) > 1) s
После чего мы объединим все списки в один, удалив из них один элемент.
concat (map tail s)
Это может быть неэлегантно, однако концептуально очень просто.Я предполагаю, что он ищет последовательные повторяющиеся слова, как в примерах.
-- a wrapper that allows you to give the input as a String
repititions :: String -> [String]
repititions s = repititionsLogic (words s)
-- dose the real work
repititionsLogic :: [String] -> [String]
repititionsLogic [] = []
repititionsLogic [a] = []
repititionsLogic (a:as)
| ((==) a (head as)) = a : repititionsLogic as
| otherwise = repititionsLogic as
Основываясь на том, что ответил Александр Прокофьев:
repetitions x = concat (map tail (filter (\x -> (length x) > 1) (List.group (word (filter (\c -> (c >= 'a' && c <= 'z') || (c>='A' && c <= 'Z') || c==' ') x)))))
Удалите ненужную скобку:
repetitions x = concat (map tail (filter (\x -> length x > 1) (List.group (word (filter (\c -> c >= 'a' && c <= 'z' || c>='A' && c <= 'Z' || c==' ') x)))))
Используйте $, чтобы удалить дополнительные круглые скобки (каждый $ может заменить открывающую скобку, если конечная скобка находится в конце выражения):
repetitions x = concat $ map tail $ filter (\x -> length x > 1) $ List.group $ word $ filter (\c -> c >= 'a' && c <= 'z' || c>='A' && c <= 'Z' || c==' ') x
Замените диапазоны символов функциями из Data.Char, объедините concat и карту:
repetitions x = concatMap tail $ filter (\x -> length x > 1) $ List.group $ word $ filter (\c -> isAlpha c || isSeparator c) x
Используйте разделы и каррирование в стиле без точек для упрощения. (\x -> length x > 1) to ((>1) . length)
.Это сочетает в себе length
с (>1) (частично применяемый оператор или раздел) в конвейере справа налево.
repetitions x = concatMap tail $ filter ((>1) . length) $ List.group $ word $ filter (\c -> isAlpha c || isSeparator c) x
Устраните явную переменную «x», чтобы сделать общее выражение свободным от точек:
repetitions = concatMap tail . filter ((>1) . length) . List.group . word . filter (\c -> isAlpha c || isSeparator c)
Теперь вся функция, читающая справа налево, представляет собой конвейер, который фильтрует только альфа-символы или символы-разделители, разбивает их на слова, разбивает на группы, фильтрует группы, содержащие более 1 элемента, а затем сводит оставшиеся группы к первой. элемент каждого.