从昨天下午学完列表生成式就开始琢磨这个 生成器 + 列表生成式 输出杨辉三角
话不多说先放廖神的学习链接:https://www.liaoxuefeng.com/wiki/1016959663602400/1017318207388128
起初还在抱怨廖神每次给的题都这么头疼,第一次是递归函数的汉诺塔(说来惭愧,我现在也没想明白哈哈哈),这次给的杨辉三角(又称帕斯卡三角)虽然看的明白但是也开始也不知道咋用代码写出来,后来吧经过长时间的看回答搜索问题终于弄懂了,而且还整明白了其他几个没掌握的知识点。所以举一反三真的很重要,但是遇到看不明白的也要学会放下(比如我的汉诺塔TAT)。
废话完了开始正题:
生成器按我的理解就是将List、Dic、Tuple这些元组中有规律的那一部分不以数据的形式储存,而是仅储存其算法,因此可以节约大量的内存空间,毕竟List输出的是一串数据会占用极大的内存空间
而生成器输出的只是一串内存的位置:<generator object triangles at 0x000001FDA210E510>,在这个地方保留了算法,需要调用的时候就重新计算一下,当然这些都只是性质上的,那么实际操作上应该如何进行呢?
生成Generator有两种方式,一种是将列表生成式的 [ ] 直接重新赋值改为 ( ) 就成为了一个Generator,另一种是接下来要说到的使用 yield 语法。
第一种举例(这里直接引用廖神的举例,我脑子不够就不做无谓的浪费了):
>>> L = [x * x for x in range(10)]
>>> L
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
>>> g = (x * x for x in range(10))
>>> g
<generator object <genexpr> at 0x1022ef630>
可以很明显的看到仅仅是改变了括号的形式,但是类型和输出结果都完全变化了。
第二种就直接跟对杨辉三角的理解来说了吧,首先放上杨辉三角:
1
/ \
1 1
/ \ / \
1 2 1
/ \ / \ / \
1 3 3 1
/ \ / \ / \ / \
1 4 6 4 1
/ \ / \ / \ / \ / \
1 5 10 10 5 1
这个 / 和 \ 都可以理解为合并,源头的数合并为尽头的数。
百度百科中Python部分的代码写的比我的有灵性多了,可以参考参考:https://baike.baidu.com/item/%E6%9D%A8%E8%BE%89%E4%B8%89%E8%A7%92/215098?fromtitle=%E6%9D%A8%E8%BE%89%E4%B8%89%E8%A7%92%E5%BD%A2&fromid=5409623&fr=aladdin#4_10
我琢磨的代码是:
def triangles_one(max):
L = [1]
if max == 1:
return L
L = [0, 1, 0]
n = 1
while n < max:
n = n + 1
L1 = [0]
for i in range(len(L)-1):
L1.append(L[i]+L[i+1])
L1.append(0)
L = L1
return L[1:len(L)-1]
来说一下这个代码的思路,我将每一行最前都加了个0(看了百度百科后发现这一步是多余的),就是一开始就定义无论如何while我的L1都是从 [0] 干起,于是每行的一个数一定是0,第二个数一定是 0 号位和 1 号位相加。
于是有了 L1.append(L[i]+L[i+1]) ,中间的那句 for i in range(len(L)-1) 就是规定了这个 append 的数是从上一个 L 的 0 号位和 1 号位相加开始的。同时我也是才知道range不规定起始位置就从0开始取,同样的range第一篇提到过是包头不含尾的。
加工完中间的数据,我们最后一个数如果不在上一个L的末尾进行 append(0) 就会使 L1.append(L[i]+L[i+1]) 中的 L[i+1] 这个超出,因为 i 是最后一个数,所以需要在末尾进行 append(0) 这个操作。
最后因为首尾各加了一个多余的0用来辅助(这里的思路就很想Excel里的辅助列),但是在正式的结果中是不需要这个辅助的,于是我们利用list的取数规则 L[1:len(L)-1] 对L进行去头舍尾。
但是这个代码只能输出max那一层的结果,无法顺利的提取全部的结果(比如要求要1至5层每一层的结果就没办法了),很没有灵性,那稍微改一下:
def triangles_each(max):
print([1])
if max == 1:
return 'done'
L = [0, 1, 0]
n = 1
while n < max:
n = n + 1
L1 = [0]
for i in range(len(L)-1):
L1.append(L[i]+L[i+1])
L1.append(0)
L = L1
print(L[1:len(L)-1])
return 'done'
print(triangles(5))
#输出:
#[1]
#[1, 1]
#[1, 2, 1]
#[1, 3, 3, 1]
#[1, 4, 6, 4, 1]
#done
但是这样需要不停的将数据加到list中(你没有看到是因为我懒....),也占用内存,很不划算,而且代码很笨重,于是这个时候我们终于可以进入到正题了,使用 yield 语句将这个函数变为一个 Generator 。
代码如下:
def triangles_grt(max):
yield [1]
L = [0, 1, 0]
n = 1
while n < max:
n = n + 1
L1 = [0]
for i in range(len(L)-1):
L1.append(L[i]+L[i+1])
L1.append(0)
L = L1
yield L[1:len(L)-1]
这个 yield 的意思按我的理解就是收录,就是告诉计算机你收录一下这个结果,类似list的append,yield 的英语我看了一下是屈服、停止争论,对比上面的代码每个结果都要输出,而yield了之后就保存起来了,相当于 emmm 隐忍吧哈哈。反正就是告诉计算机:闭嘴吧你,记着就好了。于是就变成了一个Generator。
到这里都是怎么生成Generator,以及笨办法整杨辉三角形,现在来看看我在百度百科里看到的使用列表生成式结合 yield 和我新学会的 sum 和 zip 函数(有点绕哈,但是代码这个事情吧确实不懂的时候云里雾里,懂了就是腾云驾雾哈哈哈),以下是正题,包括一些我的学习小方法:
首先放上列表生成式的结果(到目前为止每一段代码我都是现场手打出来,因为只有这样才能真正的属于自己,即使过后忘记了,但等到需要的时候也能快速的捡起来):
def triangles_cpn():
L = [1]
while 1:
yield L
L = [sum(i) for i in zip([0]+L, L+[0])]
这个里面主要用到的思维是错位相加,同时错位相加是用这个 zip 函数实现的,关于 zip 函数可以看看Runoob的教程:
https://www.runoob.com/python/python-func-zip.html
我的习惯就是拿到一个函数先在编译器里面试一下,比如这个 zip 函数,我试验了:
L = [1,2,1]
t = zip([0]+L, L+[0])
for i in t:
print(i)
#以下是输出结果:
#(0, 1)
#(1, 2)
#(2, 1)
#(1, 0)
这个就是错位相加,不知道大家是否又发现这实际上这个输出结果就是一个错位相加,同时与上面的 L1.append(L[i]+L[i+1]) 异常神似,同时很完美缩减了行数,当然,到 zip 函数这里其实也只是实现了错位,sum 函数实现了相加,sum 会将一个list中的所有元素求和(但是这里要注意list中只能包含 float 和 int ,如果有 str 之类的就会返回一个错误)。
这个时候再去看上面的 列表生成式 + yield生成器 def 的杨辉三角形函数,就全部可以理解,并且发现很有灵性。
个人认为一段代码如果能逻辑清晰的写出来当然最好,不过能这样具有灵性的表达出来会使代码更迷人哈哈。