如何在haskell中同时使用2个列表

时间:2013-12-26 12:54:22

标签: list haskell

我正在尝试解决有关字母表的haskell练习。给定一个新的字母顺序的单词列表,找到这个新的字母表。

例如,给定["ab","abd","abc","ba","bd","cc"]字样, 新的可能字母是"abdc""adbc"

我开始计算所有可能的字母顺序

alfabet :: Eq a => [[a]] -> [[a]]
alfabet list = permutations $ nub $ concat $ list

在此之后,我想我应该过滤掉那些错误的字母表,但我似乎无法传递足够的信息。我尝试使用内置的filter函数,我尝试编写自己的排序函数,这样当我按新顺序对单词进行排序时,结果列表与输入列表相同,因此字母是正确的。一切都没有用。

我想我遇到的最大问题是我需要能够同时使用两个列表(单词和不同的字母)并以不同的方式循环它们。

任何提示或帮助?感谢

2 个答案:

答案 0 :(得分:4)

解决这个问题的方法不止一种。您建议的方法是在您拥有的字母上生成所有可能的字母,然后将其过滤掉哪些字母与示例数据一致。我会先告诉你一种方法。

另一种方法是将示例数据中的信息提炼成一些关于字母可以进入的顺序的信息(数学家称之为部分排序),然后将其扩展到所有可能的排序。

方法1:过滤所有可能的字母

import Data.List (permutations, nub, sort)

排序

我将使用类型同义词Alphabet来清楚哪些列表是潜在的字母表,哪些是单词,并根据字母表定义排序(byAlphabet),并将其扩展为适用于按lexiographic排序的列表。

type Alphabet a = [a]

byAlphabet :: Eq a => Alphabet a -> a -> a -> Ordering
byAlphabet alphabet x y 
    | x == y = EQ 
    | otherwise = if y `elem` (dropWhile (/=x) alphabet) then LT else GT

lexiographic :: (a->a->Ordering) -> [a]->[a]->Ordering
lexiographic cmp [] [] = EQ
lexiographic cmp [] _ = LT
lexiographic cmp _ [] = GT
lexiographic cmp (x:xs) (y:ys) = case cmp x y of
    EQ -> lexiographic cmp xs ys
    x -> x

检查示例数据

我们需要检查给定数据的给定列表是否为consistentWith

consistentWith :: Eq a => [[a]] -> Alphabet a -> Bool
consistentWith xss alphabet = all (/=GT) $ 
       zipWith (lexiographic $ byAlphabet alphabet) xss (tail xss)

您似乎很难在潜在字母表列表中使用它,但确实知道您可以使用filter

anyOKby :: Eq a => [[a]] -> [Alphabet a] -> [Alphabet a]
anyOKby sortedWords = filter (consistentWith sortedWords)

解决方案

提供稍加编辑的alfabet功能,过滤掉不起作用的功能。

alfabet :: Eq a => [[a]] -> [Alphabet a]
alfabet list = anyOKby list $ permutations $ nub $ concat $ list

example = ["ab","abd","abc","ba","bd","cc"]

这可以按预期工作:

ghci> byAlphabet "abc" 'c' 'a'
GT
ghci> lexiographic (byAlphabet "abc") "ccba" "ccbc"
LT
ghci> consistentWith example "abcd"
False
ghci> consistentWith example "abdc"
True
ghci> alfabet example
["abdc","adbc"]

现在这是一种相当慢的方式,因为它会生成许多潜在的字母表,然后慢慢过滤掉它们。我第一次尝试,我放弃了等待alfabet (sort $ words "hello there the their he and at ah eh")打印。


方法2:找到部分排序并将其扩展

我将使用数据类型来显示哪些字符在其他字符之前,因此'a' :<: 'b'表示'a'必须在字母表中'b'之前

data CMP a = a :<: a deriving (Eq,Show)

来自示例数据的比较事实

我将使用[CMP a]代替Maybe (CMP a)只是因为concatimport Data.Maybe (catMaybes)更容易,但是每对相邻的单词最多只能提供一个fact关于字母表的比较factszipWith f xs (tail xs)函数使用漂亮的f技巧来使用justTheFirst :: [a] -> [a] justTheFirst [] = [] justTheFirst (a:_) = [a] fact :: Eq a => [a] -> [a] -> [CMP a] fact xs ys = justTheFirst . filter neq $ zipWith (:<:) xs ys where neq (a:<:b) = a /= b facts :: Eq a => [[a]] -> [CMP a] facts xss = nub . concat $ zipWith fact xss (tail xss) 从列表中的每个相邻对中创建一个东西。

ghci> fact "wellbeing" "wellington"
['b' :<: 'i']
*Main ghci> facts example
['d' :<: 'c','a' :<: 'b','a' :<: 'd','b' :<: 'c']

<强>示例:

facts

部分订购

我们将使用数据类型来表示部分排序 - 字符列表和一组比较,我们将使用nub.concat函数从示例排序的单词和您的{生成比较{1}}获取信件的技巧:

data Partial a = Partial {chars :: [a], order :: [CMP a]} deriving Show

partial :: Eq a => [[a]] -> Partial a
partial xss = Partial {chars = nub $ concat xss, order = facts xss}

示例:

ghci> partial example
Partial{chars = "abdc",order = ['d' :<: 'c','a' :<: 'b','a' :<: 'd','b' :<: 'c']}

潜在的最小元素

要从部分排序中增加可能的字母表列表,我们首先需要找到哪些元素可以放在前面。只要你不比任何东西都大,就可以站在前面,所以让我们列出nonBigs。如果我们将潜在的第一个字母放在字母表的前面,我们可以从剩余的部分顺序中remove

nonBigs :: Eq a => [CMP a] -> [a] -> [a]
nonBigs lts as = filter (not.big) as where
   big a = a `elem` (map (\ (_ :<: a) -> a) lts)

remove :: Eq a => a -> [CMP a] -> [CMP a]
remove a = filter no_a where
   no_a (x :<: y) = not $ a `elem` [x,y]

示例:(唯一不比示例中的内容更重要的是'a',并且有两个事实不具备'a'

ghci> facts example
['d' :<: 'c','a' :<: 'b','a' :<: 'd','b' :<: 'c']
ghci> nonBigs (facts example) "abcd"
"a"
ghci> remove 'a' (facts example)
['d' :<: 'c','b' :<: 'c']

让我们将nonBigs与部分排序配对,删除该字母以获取所有可能的最小元素以及如何从那里继续:

minima :: Eq a => Partial a -> [(a,Partial a)]
minima (Partial as lts) = 
   [(a,Partial (filter (/=a) as) (remove a lts) )|a <- nonBigs lts as]

示例:您必须先在示例中使用'a',但之后您可以'b''d'

ghci> minima $ partial example
         [('a',Partial {chars = "bdc", order = ['d' :<: 'c','b' :<: 'c']})]
ghci> minima $ Partial {chars = "bdc", order = ['d' :<: 'c','b' :<: 'c']}
         [('b',Partial {chars = "dc", order = ['d' :<: 'c']}),
          ('d',Partial {chars = "bc", order = ['b' :<: 'c']})]

解决方案

复杂的比特正在使用偏序给出的“有向图”来增长所有可能的树状路径。我们将使用树生长函数f :: input -> [(output,input)]来告诉您所有可能的继续进行的方法。如果这不能给你任何答案,我们需要[[]],一个空的路径,我们将通过在每个可能性的前面(map (o:))放置可能的第一个元素来递归地增长({{1} }):

treePaths f i'

示例: treePaths :: (input -> [(output,input)]) -> input -> [[output]] treePaths f i = case f i of [] -> [[]] pairs -> concat [map (o:) (treePaths f i') | (o,i') <- pairs] alphabets list = treePaths minima (partial list) 长度计算几乎是即时的,但alphabets长度计算在我的(相当旧的)笔记本电脑上需要超过2分钟;只生成所需的输出比生成每个输出和丢弃更快。

alfabet

答案 1 :(得分:1)

如果你采取\ls -> map head $ group $ map head ls并在["ab","abd","abc",ba","bd",cc"]上使用"abc",那就会给你"b"。如果您要为第二个符号重复类似的过程,其中第一个符号相同,那么您还有"ad""c"('a', 'b')。这些部分字母表足以找出所有可能的完整字母表。

我建议使用所有部分字母表来构建对的列表(集合),这样如果列表中有'a',那么'b'会先于{{1}}。然后将符号逐个插入所有可能的位置,使用该集作为比较器。一定要考虑不确定的订单。