哈斯克尔:两个单词之间共用字母

时间:2017-10-24 22:54:19

标签: arrays algorithm haskell duplicates

我刚开始学习Haskell。我正在尝试获取两个单词之间所有常用字母的列表,例如"hello""llama" [ 'l', 'l' ]"happy"和{{1} },"pay"

我尝试使用相交,但我遇到重复问题,[ 'a', 'p', 'y' ]"happy"导致"pay"。我不能删除重复项,因为它们可以存在,如第一个示例所示。

如果有任何建议,我将不胜感激。谢谢!

4 个答案:

答案 0 :(得分:3)

您可以使用multiset包:

Data.MultiSet> fromList "hello" `intersection` fromList "llama"
fromOccurList [('l',2)]
Data.MultiSet> fromList "happy" `intersection` fromList "pay"
fromOccurList [('a',1),('p',1),('y',1)]

data-ordlist包还提供此功能:

Data.List Data.List.Ordered> sort "hello" `isect` sort "llama"
"ll"
Data.List Data.List.Ordered> sort "happy" `isect` sort "pay"
"apy"

答案 1 :(得分:0)

我认为这是使用Data.Map的理想情况。我将按如下方式实现:

import qualified Data.Map.Lazy as M

sharedLetters :: String -> String -> String
sharedLetters s1 s2 = let cm = foldr (checkMap (\(x,y) -> (x,y+1))) charMap s2
                               where checkMap f c m = if M.member c m then M.adjust f c m
                                                                      else M.insert c (f (0,0)) m
                                     charMap        = foldr (checkMap (\(x,y) -> (x+1,y))) M.empty s1
                      in M.foldlWithKey (\r k (v1,v2) -> r ++ replicate (minimum [v1,v2]) k) "" cm

main :: IO String
main = do
  putStr "Enter first string  :"
  s1 <- getLine
  putStr "Enter second string :"
  s2 <- getLine
  return $ sharedLetters s1 s2

Enter first string  :happy
Enter second string :pay
"apy"

Enter first string  :pay
Enter second string :happy
"apy"

Enter first string  :hello
Enter second string :llama
"ll"

Enter first string  :llama
Enter second string :hello
"ll"

答案 2 :(得分:0)

这是一项值得学习的好技巧。假设您有两个排序列表:

[1,1,5,10,15,15,18]
[2,5,8,10,15,20]

并且您希望将它们合并到一个排序列表中。在Haskell中,使用模式匹配和保护来编写此算法是一种非常优雅的方法:

merge (x:xs) (y:ys) | x < y     =  x : merge xs (y:ys)
                    | otherwise =  y : merge (x:xs) ys
merge xs [] = xs
merge [] ys = ys

这样:

> merge [1,1,5,10,15,15,18] [2,5,8,10,15,20]
[1,1,2,5,5,8,10,10,15,15,15,18,20]
> 

简而言之,当两个列表都非空时,它会比较两个列表的头部并输出最小的头部;然后它使用递归输出“其余的”。

它也可以用三种情况(更少,更大,更平等)写成所有明确的:

merge (x:xs) (y:ys) | x < y     =  x : merge xs (y:ys)
                    | x > y     =  y : merge (x:xs) ys
                    | otherwise =  y : merge (x:xs) ys
merge xs [] = xs
merge [] ys = ys

这个通用模板可用于在排序列表上实现许多有趣的算法。这是删除常用元素的元素,例如:

uncommon (x:xs) (y:ys) | x < y     =  x : uncommon xs (y:ys)
                       | x > y     =  y : uncommon (x:xs) ys
                       | otherwise =  uncommon xs ys
uncommon xs [] = xs
uncommon [] ys = ys

这样:

> uncommon [1,1,5,10,15,15,18] [2,5,8,10,15,20]
[1,1,2,8,15,18,20]
>

您可能希望尝试修改uncommon函数以创建diff函数,该函数输出从第一个列表中删除第二个列表元素的结果。它需要修改前三个受保护案例中的一个,你还需要调整两个“空列表”模式匹配中的一个:

> diff [1,1,5,10,15,15,18] [2,5,8,10,15,20]
[1,1,15,18]
>

一旦你弄明白这一点,你会发现创建一个common函数很容易,它输出两个排序列表的共享元素给出:

> common [1,1,5,10,15,15,18] [2,5,8,10,15,20]
[5,10,15]
>

由于字符串只是字符列表,因此使用sort中的Data.List对列表进行预排序也可以解决您的问题:

> import Data.List
> common (sort "hello") (sort "llama")
"ll"
> common (sort "happy") (sort "pay")
"apy"
> 

答案 3 :(得分:0)

利用这些单词之间共享的每个字母(允许重复)的事实如何显示为由这些单词的结合形成的集合中的一对字母?您可以通过对联合集进行排序并选择重复项来有效地找到这些对 -

let find_dups ([]) = []; find_dups (x:y:xs) | x == y = x:find_dups(xs); find_dups (x:xs) = find_dups(xs)                                              
let common_letters word1 word2 = find_dups (sort (word1 ++ word2))

> common_letters "hello" "fellows"
"ello"