首先为三角形中的每个元素分配一个索引:
| 0 1 2 3 4 5 6
--+--------------
0 | 1
1 | 1 1
2 | 1 2 1
3 | 1 3 3 1
4 | 1 4 6 4 1
5 | 1 5 10 10 5 1
6 | 1 6 15 20 15 6 1
在这里,我只是将三角形放在一边,以便我们对它们进行编号。所以在这里我会说元素 at (6, 4)
is 15
, while(4, 6)
不存在。现在专注于写一个函数
pascal :: Integer -> Integer -> Integer
pascal x y = ???
这样您就可以生成此版本的三角形。你可以从写作开始
pascal x y
| x == 0 = 1
| x == y = 1
| x < y = error "Not a valid coordinate for Pascal's triangle."
| otherwise = pascal ? ? + pascal ? ?
请注意,在这里,您可以通过直角坐标来确定哪些元素应该通过对角线添加在一起,而不是确定。在这里,您会注意到您y
所在的三角形中的哪一行,以及该行中x
元素的位置。您需要做的就是找出代替?
s 的内容。
一旦你开始工作,我就为这个三角形提供了一个更有效的单线,并且可以在仍然使用递归的同时一次生成整个三角形:
import Data.List (scanl1)
pascals :: [[Integer]]
pascals = repeat 1 : map (scanl1 (+)) pascals
不要试图把这个解决方案交给你的教授,这不是他们想要的,如果你只用了一周的 Haskell,就会很明显有人给了你这个解决方案。但是,它确实显示了 Haskell 对此类问题的强大功能。我将展示如何通过索引pascals
来获得给定的(n, k)
值,但这样做也会为您提供太多解决朴素递归的提示。
由于存在一些混淆,我给出这个解决方案的原因是为了在它和斐波那契序列的经常显示的惰性实现之间画一个平行线:
fibs = 1 : 1 : zipWith (+) fibs (tail fibs)
相比
fib 0 = 1
fib 1 = 1
fib n = fib (n - 1) + fib (n - 2)
这个定义生成了所有斐波那契数的无限列表,并且这样做非常有效(从 CPU 的角度来看,RAM 是另一回事)。它在其前两个元素中编码基本情况,然后是可以计算其余部分的递归表达式。对于斐波那契,您需要 2 个值才能开始,但对于帕斯卡三角形,您只需要一个值,该值恰好是一个无限列表。我在上面发布的网格中的列之间有一个很容易看到的模式,该scanl1 (+)
函数只是利用了这种模式并允许我们非常容易地生成它,但这是生成三角形的对角线而不是行。要获取行,你可以索引这个列表,或者你可以做一些花哨的技巧take
,drop
和其他类似功能,但这是另一天的练习。