Haskell过滤掉循环排列

时间:2018-05-05 19:02:52

标签: haskell

您有一个包含N个元素的列表 您只想打印不是同一列表中其他元素的循环元素的元素

要检查两个字符串是否是彼此的循环排列,我这样做,这很好用:

string1 = "abc"
string2 = "cab"
stringconc = string1 ++ string1
if string2 `isInfixOf` stringconc
then -- it's a circular permuation
else -- it's not

编辑:正如一条评论指出的那样,此测试仅适用于相同大小的字符串

回到真实用例:

checkClean :: [String] -> [String] -> IO String
checkClean [] list = return ""
checkClean (x:xs) list = do
let sequence = cleanInfix x list
  if sequence /= "abortmath"
    then putStr sequence
    else return ()
  checkClean xs list

cleanInfix:

cleanInfix :: String -> [String] -> String
cleanInfix seq [] = seq
cleanInfix seq (x:xs) = do
  let seqconc = x ++ x
  if seq `isInfixOf` seqconc && seq /= x
    then "abortmath"
    else cleanInfix seq xs

然而,这只是输出......没有 通过一些研究,我发现checkClean中的序列始终是“abortmath” 此外,我对这种“旗帜”流产感到不太满意,因为如果列表中的一个元素是“abortmath”,那么很好......

例如: 如果我有一个由以下内容组成的清单:

NUUNNFFUF
FFUFNUUNN

我应该写 NUUNNFFUF

2 个答案:

答案 0 :(得分:0)

你可以用钢笔和纸张制作奇迹......

所以,如果有人对此感兴趣,我是如何解决它的,它可能已经被严格优化但至少它可以工作(我只是想学习haskell,所以它现在已经足够好了)

-- cleanInfix function
cleanInfix :: String -> [String] -> [String] -> [String]                                                                    
cleanInfix sequence [] cleanlist = cleanlist                                                                           
cleanInfix sequence (x:xs) cleanlist = do 
-- this is where I check for the circular permuation                                                                                  
  let sequenceconc = x ++ x                                                                                                   
  if sequence `isInfixOf` sequenceconc                                                                                        
    then cleanInfix sequence xs (delete x cleanlist)                                                                        
    else cleanInfix sequence xs cleanlist                                                                                   


-- checkClean Function
checkClean :: [String] -> [String] -> [String] -> [String]                                                                  
checkClean [] listesend cleanlist = cleanlist                                                                            
checkClean (x:xs) listesend cleanlist = do
-- The first delete is to avoid checking if an element is the circular permuation of... itself, because it obviously is... in some way                                                                                  
  let liste2 = cleanInfix x (delete x listesend) cleanlist                                                                  
  checkClean xs (delete x listesend) liste2 


 -- Clean function, first second and third are the command line argument don't worry about them                                                                                                                        
clean first second third = do                                                                                                 
  -- create of the result list by asking user for input

  let printlist = checkClean result result result -- yes, it's the same list, three times                                                                            
  print printlist -- print the list

答案 1 :(得分:0)

我猜你用这样的东西调用你的初始代码(问题):

result = ["NUUNNFFUF", "FFUFNUUNN"]
main = do
    checkClean result result

它不会打印任何内容,因为:

  • cleanInfix的第一次调用的参数有以下参数:"NUUNNFFUF"["NUUNNFFUF", "FFUFNUUNN"]
  • cleanInfix中,因为seq == x您使用以下参数进行递归调用:"NUUNNFFUF"["FFUFNUUNN"]
  • 现在,"NUUNNFFUF""FFUFNUUNN"的真实排列:cleanInfix返回"abortmath"checkClean返回()
  • 然后您通过以下参数递归调用checkClean"FFUFNUUNN"["NUUNNFFUF", "FFUFNUUNN"]
  • 再次,"FFUFNUUNN""NUUNNFFUF"的真实排列:cleanInfix返回"abortmath"checkClean返回()
  • 这是结束。

基本上,x是y的排列,y是x的排列,因此x和y被丢弃。

你的答案有效,但它非常复杂。

我不会尝试改进您的任何代码,但我会做一般性评论:您应该(您真的应该)避免在您不需要时返回monad:in问题,checkClean只需要从列表中删除重复项(或“循环重复项”)。这完全是功能性的:您拥有所需的所有信息。因此,请删除doletreturn s!

现在,让我们试着关注这个:

  

您有一个包含N个元素的列表您只想打印不是同一列表中其他元素的循环元素的元素

为什么不使用关于循环排列的初步知识?

isCircPermOf x y = x `isInfixOf` (y ++ y)

现在,您需要一个接受序列和序列列表的函数,并且只返回第二个元素不是第一个的循环排列:

filterCircDuplicates :: String -> [String] -> [String]
filterCircDuplicates seq [] = []
filterCircDuplicates seq (x:xs) =
    if seq `isCircPermOf` x
    then removeCircDuplicates seq xs
    else x:removeCircDuplicates seq xs

这种模式众所周知,您可以使用filter来简化它:

filterCircDuplicates seq l = filter (\x -> !seq `isCircPermOf` x) l

或更好:

filterCircDuplicates seq = filter (not.isCircPermOf seq) 

请注意签名:not.isCircPermOf seq :: String -> Boolean。如果当前元素不是seq的循环排列,则返回true。 (您不必添加列表参数。)

最后一步:您需要一个获取列表并返回此列表但没有(循环)重复项的函数。

removeCircDuplicates :: [String] -> [String]
removeCircDuplicates [] = []
removeCircDuplicates (x:xs) = x:filterCircDuplicates x (removeCircDuplicates xs)

当你的列表有头部和尾部时,你清理尾部,然后删除尾部第一个元素的副本,并保留第一个元素。

同样,你有一个众所周知的模式,fold

removeCircDuplicates = foldr (\x acc -> x:filterCircDuplicates x acc) []

从右到左删除重复项。

如果你想要一个单行:

Prelude Data.List> foldr (\x -> ((:) x).filter(not.(flip isInfixOf (x++x)))) [] ["abcd", "acbd", "cdab", "abdc", "dcab"]
["abcd","acbd","abdc"]