繁体   English   中英

在Haskell中生成所有可能的路径

[英]Generating All Possible Paths in Haskell

我的措辞非常糟糕,所以请耐心等待。

我正在做一个问题,需要我在Haskell中以列表列表的形式生成所有可能的数字。

例如,如果我有x = 3和y = 2,我必须生成一个列表列表,如下所示:

[[1,1,1], [1,2,1], [2,1,1], [2,2,1], [1,1,2], [1,2,2], [2,1,2], [2,2,2]]

x和y被传递给函数,它必须使用任何非零正整数x和y。

我完全迷失了,不知道怎么开始。

对于任何有兴趣帮助我的人,请尽量使用尽可能容易理解的数学解释。 我真的不擅长数学。

假设这是作业,我会给你答案的一部分,并告诉你我如何通过这类问题思考。 在GHCi中进行实验并构建我们需要的部分是有帮助的。 我们需要的一件事是能够生成从1到y的数字列表。 假设y是7.那么:

λ> [1..7]
[1,2,3,4,5,6,7]

但正如您稍后会看到的,我们真正需要的不是一个简单的列表,而是一个我们可以构建的列表列表。 像这样:

λ> map (:[]) [1..7]
[[1],[2],[3],[4],[5],[6],[7]]

这基本上是指取数组中的每个元素,并将其添加到空列表[] 所以现在我们可以编写一个函数来为我们这样做。

makeListOfLists y = map (:[]) [1..y]

接下来,我们需要一种方法将新元素添加到列表列表中的每个元素。 像这样的东西:

λ> map (99:) [[1],[2],[3],[4],[5],[6],[7]]
[[99,1],[99,2],[99,3],[99,4],[99,5],[99,6],[99,7]]

(我在这里用的是99而不是1,所以你可以很容易地看到数字的来源。)所以我们可以编写一个函数来做到这一点:

prepend x yss = map (x:) yss

最终,我们希望能够获取列表和列表列表,并在列表中的每个元素上调用prepend列表中的每个元素。 我们可以再次使用map函数。 但事实证明,如果我们将参数的顺序切换为prepend将会更容易,如下所示:

prepend2 yss x = map (x:) yss

然后我们可以这样做:

λ>  map (prepend2 [[1],[2],[3],[4],[5],[6],[7]]) [97,98,99]
[[[97,1],[97,2],[97,3],[97,4],[97,5],[97,6],[97,7]],[[98,1],[98,2],[98,3],[98,4],[98,5],[98,6],[98,7]],[[99,1],[99,2],[99,3],[99,4],[99,5],[99,6],[99,7]]]

所以现在我们可以编写该函数:

supermap xs yss = map (prepend2 yss) xs

使用您的示例,如果x = 2且y = 3,那么我们需要的答案是:

λ> let yss = makeListOfLists 3
λ> supermap [1..3] yss
[[[1,1],[1,2],[1,3]],[[2,1],[2,2],[2,3]],[[3,1],[3,2],[3,3]]]

(如果这就是我们所需要的全部内容,我们可以使用列表理解更轻松地完成此操作。但由于我们需要能够为任意x执行此操作,因此列表理解不起作用。)

希望您可以从这里获取它,并将其扩展到任意x。

对于特定的x,如前所述,列表理解可以解决问题,假设x等于3,可以编写以下内容:

 generate y = [[a,b,c] | a<-[1..y], b<-[1..y], c <-[1..y]]

但是当x没有预先确定时,生活变得复杂得多。 我在Haskell中没有太多的编程经验,我不熟悉库函数,我的方法远不是最有效的解决方案,所以不要过于严厉地判断它。

我的解决方案包括两个功能:

strip [] = []
strip (h:t) = h ++ strip t

populate y 2 = strip( map (\a-> map (:a:[]) [1..y]) [1..y])
populate y x = strip( map (\a-> map (:a) [1..y]) ( populate y ( x - 1) ))

strip是为嵌套列表定义的。 通过合并列表项,它可以减少层次结构。 例如打电话

strip [[1],[2],[3]]

生成输出:

[1,2,3]

填充是棘手的。

在递归的最后一步,当第二个参数等于2时,函数将[1..y]的每个项目与同一列表中的每个元素映射到一个新列表中。 例如

map (\a-> map (:a:[]) [1..2]) [1..2])

生成输出:

[[[1,1],[2,1]],[[1,2],[2,2]]]

并且条带功能将其转换为:

[[1,1],[2,1],[1,2],[2,2]]

至于递归的初始步骤,当x大于2时,populate几乎完全相同,除了这次它将列表的项目与递归调用生成的列表进行映射。 最后:

populate 2 3

给我们想要的结果:

[[1,1,1],[2,1,1],[1,2,1],[2,2,1],[1,1,2],[2,1,2],[1,2,2],[2,2,2]]

正如我上面提到的,这种方法既不是最有效也不是最可读的方法,但我认为它解决了这个问题。 实际上,理论上解决这个问题的唯一方法是在没有大量使用递归的情况下构建带有list comprehension语句的字符串,而不是动态编译该字符串,根据我的短暂经验,作为程序员,它永远不会是一个好的解。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM