Haskell:穿过三角形的探路者

时间:2013-12-06 19:21:51

标签: haskell

所以假设你有一个三角形:

triangle = ["1"
           ,"2 3"
           ,"4 5 6"]

这可以表示为:

[[1],[2,3],[4,5,6]]

我正在尝试实现一个函数,通过仅遍历调整后的数字来查找此三角形中的每条可能路径,即1和5中的2和3以及3中的6。

例如,应用于上面的int列表列表,结果将是:

[[1,2,4],[1,2,5],[1,3,5],[1,3,6]]
到目前为止,我的努力非常糟糕:

findRoutes :: [[Int]]->[[Int]]
findRoutes [] = []
findRoutes (x:xs) = **find all possible paths with x as the head **:(findRoutes xs)

我真的不确定如何进步,任何提示家伙?

2 个答案:

答案 0 :(得分:4)

TL; DR :所有重要的代码都在最后。

使用一些高阶函数必须有一个很好的干净方法,所以这里:

安排:

  • 使用foldr
  • 构建路线
  • 将当前顶点放在所有可能的前进路线前面
  • 狡猾地使用zipWith (++)用它们的尾部拉上前进的路线,这样你就可以在下行时向左或向右走。
  • 首先将最后一个点列表转换为单点路线列表

示例数据

example = ["11","21 22","31 32 33","41 42 43 44"]

让我们将其作为列表列表而不是使用空格来分隔:

readificate :: [String] -> [[Int]]
readificate = map (map read.words)

eg = readificate example

所以我们得到了

> eg
[[11],[21,22],[31,32,33],[41,42,43,44]]

输入programmer sanity的同义词

让我们使用整数列表来表示其中一条路径,例如[11,22,32]将是一条路径。它会让列表列表混乱,我们可能不记得每个列表的含义,所以让我们使用一两个类型的同义词来使它更清晰:

type Path a = [a]
type Routes a = [Path a]

通过在前面添加一些内容来制作新路线

我认为,如果你是通过一段时间而不是在开始或结束时,更容易想到正在发生的事情:
如果我们拥有32的所有路线,即[ [32,42], [32,43] ],那么将22放在所有路线的前面是有用的:

before :: a -> Routes a -> Routes a
a `before` rs = map (a:) rs

所以我们的例子给出了

22 `before` [ [32,42], [32,43] ]
[[21,32,42],[21,32,43]]

感觉很好,部分原因是(:)通常比!!更有效率,我们可以用它来建立解决方案。我们需要使用33中的路径以及32中的路径 - 通常我们可以去两个地方,我们希望包含这两个选项。我们可以使用++将这些组合在一起,然后在组合的前面弹出22

用尾巴压缩路线

当你需要对列表中的相邻元素进行处理时,你可以重用的一个巧妙的技巧就是用它的尾部压缩列表。例如zip [1,2,3] (tail [1,2,3]) = [(1,2),(2,3)]zipWith可让您将这些组合起来,zipWith (+) [100,20,3] (tail [100,20,3]) = [120,23]

我们将使用它将起点列表与您可以为每个起点继续的路线列表组合在一起。如果您没有任何路线可以继续,那么您只需将起点作为路线

combine :: [a] -> [Routes a] -> [Routes a]
combine starts [] = map (\a -> [[a]]) starts
combine starts routes = zipWith before starts (zipWith (++) routes (tail routes))

使用foldr构建所有路径

最后,我们可以使用foldr从列表末尾迭代回来。由于顶部只有一个顶点,因此只有一个路径列表,所以让我们使用head来获取它。

findRoutes :: [[a]] -> Routes a
findRoutes = head.foldr combine []

所以你得到了

> findRoutes eg
[[11,21,31,41],[11,21,31,42],[11,21,32,42],[11,21,32,43],[11,22,32,42],[11,22,32,43],[11,22,33,43],[11,22,33,44]]

所有重要代码在一个地方

type Path a = [a]
type Routes a = [Path a]

before :: a -> Routes a -> Routes a
a `before` rs = map (a:) rs

combine :: [a] -> [Routes a] -> [Routes a]
combine starts [] = map (\a -> [[a]]) starts
combine starts routes = zipWith before starts (zipWith (++) routes (tail routes))

findRoutes :: [[a]] -> Routes a
findRoutes = head.foldr combine []

答案 1 :(得分:0)

此功能可用于解决此问题:

getNext::[a]->[[a]]
getNext indices = [indices ++ [last indices], indices ++ [last indices+1]]

在给定Nth的情况下,它返回每个列表中元素索引的第(N + 1)条路径。例如,如果你从金字塔的尖端开始,你可以获得进入第二层的路径....

getNext [0]

产量

[[0, 0], [0, 1]]

当然,你必须从每一层中取出元素(可以使用zip和(!!)轻松完成....我会把它作为练习留给你。)

你真正想做的就是在每一层上迭代....你可以编写一个函数来执行此操作,但如果你查找数组monad,你会很快意识到>> =运算符会做这个给你

getNext [0] >>= getNext

产量

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

你可以稍微清理一下

[[0]] >>= getNext >>= getNext

(>>= getNext) $ (>>= getNext) [[0]]

因此可以通过应用(>> = getNext)N次来获得第N层路径。函数“iterate fx”返回f上的N个n个应用的无限列表,其中N是列表中的位置(即迭代fx = [x,fx,f(fx),f(f(fx)), ....]),所以这就是你想要的。

getTiers n = (iterate (>>= getNext) [[0]]) !! n