我们可以使用辅助函数pairs定义一个递归的pascal
pascal 将返回 [[Int]](一个 Int 数组数组)——例如,pascal(3) 将返回
[ [1],
[1, 1],
[1, 2, 1] ]
好的,所以我会先向您展示所有代码,然后我会逐步解释某些部分
def pairs (xs):
if 2 > len(xs):
return []
else:
return [xs[0:2]] + pairs(xs[1:])
def pascal (n):
def compute (prev):
return [1] + [x + y for (x,y) in pairs(prev)] + [1]
def aux (m, prev):
if (m > n):
return []
else:
return [prev] + aux(m + 1, compute(prev))
return aux(1, [1])
[print(line) for line in pascal(5)]
# [1]
# [1, 1]
# [1, 2, 1]
# [1, 3, 3, 1]
# [1, 4, 6, 4, 1]
说明
我们真正关心的是pascal 函数——我们编写的其他所有内容都是根据我们编写pascal 的方式产生的,所以我先来回顾一下。
编写递归函数的一种非常常见的方法是使用内部辅助函数来跟踪我们计算的各种状态。我们将使用这种技术作为 pascal 函数的基础
def my_recursive_func (<parameters>):
def aux (<state_parameters>):
if (<base_condition>):
return <base_value>
else
return aux(<updated_state>)
return aux(<initial_state>)
我们已经知道如何为 pascal 函数填写一些样板文件
-
parameters 应该只是 n,一个整数,因为我们希望调用我们的函数,如 pascal(3) 或 pascal(5) 等 - 不应接受其他参数
-
state_parameters——我们现在只知道两件事:1)我们需要一些值m,它的计数一直到n,每次递增1——和2)一些允许我们计算下一个的值排;我们称之为prev,因为每个帕斯卡行都是根据上一个行 计算的
-
base_condition – 当 m == n 我们知道我们已经生成了所有需要的行时,这就是我们想要停止递归的时候
-
base_value – 这是返回的最后一个值 – 还不太清楚应该是什么
-
updated_state - 我们将使用 m + 1 更新 m,并且我们将使用某种数组连接来更新行 - 在我们编写更多代码之前不确定
-
initial_state - 我们将在1 开始m,第一行pascal 是[1]
好的,让我们填写到目前为止的内容
def pascal (n):
def aux (m, prev):
if (m > n):
return ?
else:
return aux(m + 1, ?)
return aux(1, [1])
我们想做的是让pascal 构建我们的结果类似于这样
[[1]] + [[1, 1]] + [[1, 2, 1]] + [[1, 3, 3, 1]], ...]
# => [ [1], [1 ,1], [1, 2, 1], [1, 3, 3, 1], ... ]
所以为了写base_value 和prev 的更新状态,我们需要考虑这个返回type。我们要返回[[Int]],这是一个列表,所以base_value可以简单地是空列表[]。
这意味着在每一步中,我们实际上都希望将[prev] 和连接 (+) 连接到递归结果......
[prev] + aux(m + 1, <b><next_row></b>)
我们现在已经很接近了,让我们再次更新pascal,看看我们要完成什么
def pascal (n):
def aux (m, prev):
if (m > n):
return []
else:
return [prev] + aux(m + 1, <next_row>)
return aux(1, [1])
好的,那么难的部分来了——计算下一行,对吧?嗯,其实还不错。
# given
[1,2,1]
# the next row is
[1, (1 + 2), (2 + 1), 1]
或者另一个例子
# given
[1, 4, 6, 4, 1]
# the next row is
[1, (1 + 4), (4 + 6), (6 + 4), (4 + 1), 1]
所以模式是这样的:创建一个以1 开头的新数组,然后对于前一行中的每一对数字,将两个数字相加并将每个和附加到数组中,最后附加另一个 @ 987654368@。我们可以像使用这样的列表推导式在 python 中表达这一点
[1] + [x + y for (x,y) in pairs(prev)] + [1]
现在我们只需要弄清楚pairs 函数。 pairs 应该有如下合约
pairs([]) => []
pairs([a]) => []
pairs([a,b]) => [[a,b]]
pairs([a,b,c]) => [[a,b],[b,c]]
pairs([a,b,c,d]) => [[a,b],[b,c],[c,d]]
让我们现在实现它,并验证我们的实现是否符合合同。请注意,我在pascal 的outside 中实现了这个函数,因为它是一个通用函数并且本身就很有用。为了计算帕斯卡行,我们需要添加成对的数字,但是添加或如何我们得到这些对或数字不应该留给pascal 函数本身的责任。
def pairs (xs):
if 2 > len(xs):
return []
else:
return [xs[0:2]] + pairs(xs[1:])
print(pairs([])) # []
print(pairs([1])) # []
print(pairs([1,2])) # [[1,2]]
print(pairs([1,2,3])) # [[1,2],[2,3]]
print(pairs([1,2,3,4])) # [[1,2],[2,3],[3,4]]
好的,这让我们现在非常接近。让我们再次更新pascal 函数,看看我们在哪里
def pascal (n):
def aux (m, prev):
if (m > n):
return []
else:
return [prev] + aux(m + 1, [1] + [x + y for (x,y) in pairs(prev)] + [1])
return aux(1, [1])
神圣的烟!我们已经完成了。 aux 调用下一行的内联计算看起来有点忙。让我们在pascal 中添加另一个名为compute 的助手来清理这些东西。现在我们完成了!
def pascal (n):
def compute (prev):
return [1] + [x + y for (x,y) in pairs(prev)] + [1]
def aux (m, prev):
if (m > n):
return []
else:
return [prev] + aux(m + 1, compute(prev))
return aux(1, [1])
彻底
如果你想显示那些愚蠢的文本和提示,你可以写main 类似下面的东西——这使所有 I/O 与我们的 pascal 和 pairs 函数分开。这种关注点的分离和副作用的管理对于在程序的早期考虑很重要,因为很难重用功能超出我们的预期。
def main ():
try:
print("Pascal triangle generator")
n = int(input("Pascal(x): x = "))
if n < 1: raise
[print(line) for line in pascal(n)]
except:
print("enter a non-zero positive integer")
main()
# run program
main()
继续运行pascal(300) 或其他一些令人印象深刻的结果