在Haskell中建立指数大小的列表

时间:2019-06-16 03:36:56

标签: list haskell functional-programming pattern-matching

我有两个函数,仅当C是特定模式时才执行某些操作。 每个函数输出一个C列表。

我的目标是,给定[C],我想获得在列表中调用f1和f2的所有可能性,而其余部分保持不变。例如:

假设C的列表为:

    c1
    c2 --matches the pattern
    c3

然后我想要一个包含两个列表的列表

[[c1] ++ (f1 c2) ++ [c3],[c1] ++ (f2 c2) ++ [c3]]

但是,如果我有

c1
c2 --matches the pattern
c3 --matches the pattern

然后我们应该有4个列表,因为我们希望调用f1和f2的所有组合。 这样就可以了:

[(f1 c1) ++ (f1 c2) ++ [c3], (f2 c1) ++ (f2 c2) ++ [c3], 
(f1 c1) ++ (f2 c2) ++ [c3], (f2 c1) ++ (f1 c2) ++ [c3]]

目前,我的代码大致采用以下方式构建:

f1 :: C -> [C]

f2 :: C ->  [C]

combine ::  [C] -> [[C]]
combine  my_pattern:xs =  ?
combine (x:xs) =  ?
combine [] = []

  where first_set = (f1 my_pattern)
        second_set = (f2 my_pattern)

有人可以对我如何填补其余部分给出直觉吗? Data.List中是否有任何有用的功能?我查看了文档,但无法立即注意到哪个文档可能会有所帮助。

3 个答案:

答案 0 :(得分:2)

其他答案对我来说似乎很复杂。在这个答案中,我将进一步评论:这只是一个foldMap,结合了不确定性monad(列表!)和序列monoid(列表!)。

首先编写适用于列表中单个元素的内容:

singleElement x
    | matchesThePattern x = [f1 x, f2 x]
    | otherwise = [[x]]

然后将其应用于每个元素:

import Data.Monoid
combine = foldMap (Ap . singleElement)

就是这样。这就是整个代码。

例如,假设我们想将每个字母重复2或3次,即x-> xxxxx,而所有其他字符都保持相同。

singleElement x
    | 'a' <= x && x <= 'z' = [[x, x], [x, x, x]]
    | otherwise = [[x]]

然后我们可以在ghci中尝试它:

> combine "123def"
Ap {getAp = ["123ddeeff","123ddeefff","123ddeeeff","123ddeeefff","123dddeeff","123dddeefff","123dddeeeff","123dddeeefff"]}

当然,在您自己的代码中选择比singleElement更好的名称。

答案 1 :(得分:1)

我的方法是

  1. 解决您当前正在查看的列表中的元素的问题(xmy_pattern)。这意味着生成一个或多个新列表。
  2. 解决其余列表(xs)的问题。这将给您返回列表列表([[C]])。
  3. 合并两个解决方案。如果您从步骤1中生成了多个列表,则这些列表([C])中的每个列表都将与步骤2中列表([C])中的每个列表([[C]])合并。

我有两种可能的方法。

我不清楚您正在寻找多少帮助,因此我的回答有些“没有破坏者”。如果需要,请进行澄清或更多详细信息。

列表理解

无需深入研究ApplicativeTraversable类型类的杂草,就可以通过列表理解来完成所需的工作。

让我们考虑一下您的模式是否匹配的情况。我会写一个列表理解如下:

[ x ++ y | x <- _, y <- _] :: [[C]]
-- this means
-- x :: [C]
-- y :: [C]
-- _ :: [[C]]

此列表理解可创建列表列表。 x是前置的,因此它应该来自功能f1f2的应用。 y是每个结果列表的结尾。我会让你知道这可能是什么。

不匹配的情况比这简单,可以这样写

[ x : y | y <- _] :: [[C]]
-- note that x is not local to the list comprehension
-- y :: [C]
-- _ :: [[C]]

尽管这实际上只是上述列表理解的一个特例。

适用

解决此问题的另一种方法是使用Applicative的{​​{1}}实例。

让我们检查列表[a]实例下的函数(<*>)

Applicative

此函数具有一种奇怪的类型签名。它需要一个功能列表和一个列表,然后返回另一个列表。具有将每个功能-- this is the type when specialized to lists (<*>) :: [a -> b] -> [a] -> [b] 依次应用于a -> b的每个元素的作用。

[a]

我们想退出>>> [(+1), (+2)] <*> [1,2,3] -- [2,3,4] comes from (+1) -- [3,4,5] comes from (+2) [2,3,4,3,4,5] ,而不是[[C]],因此,如果我们想使用[C],我们可以将其类型更专门化

(<*>)

为避免混淆,我建议选择(<*>) :: [a -> [C]] -> [a] -> [[C]] ,这样可以

a = [C]

您的函数列表应该在要生成的列表上添加正确的元素。第二个参数应该是递归调用返回的列表。

答案 2 :(得分:1)

您必须拥有

applicable_f1 :: C -> Bool
applicable_f2 :: C -> Bool

以某种方式定义。然后,

combinations :: [C] -> [[C]]
combinations cs = map concat . sequence $
    [ concat $ [ [ [c]   | not (applicable_f1 c || applicable_f2 c)]
               , [ f1 c  | applicable_f1 c]
               , [ f2 c  | applicable_f2 c] ]
      | c <- cs]