昨天正好把汉诺塔小游戏中的递归给写完了,链接:汉诺塔中的递归,然后在打草稿时忽然想起来以前在别个地方看到的关于 python阴阳谜题,代码在下面:
def yin(cb1):
yield "@"
def yang(cb2):
yield "*"
yield from cb1(cb2)
yield from yang(yang)
ge = yin(yin)
for _ in range(100):
print(next(ge))
输出结果如下:
@ * @ * * @ * * * @ * * * * @ * * * * * @ * * * * * * @ * * * * * * * @ * * * * * * * * @ * * * * * * * * * @ * * * * * * * * * * @ * * * * * * * * * * * @ * * * * * * * * * * * * @ * * * * * * * * *
其中,yield
是以一个迭代器 yield from
也是一个迭代器
代码或者也有下面这个写法的
//来源于网络
def puzzle():
def yin(yin):
yield '@'
def yang(yang):
yield '*'
yield from yin(yang)
yield from yang(yang)
yield from yin(yin)
for x, _ in zip(puzzle(), range(256)):
print(x, end='')
print()
本质上是一样的,结果也一样,都是在每一个 @字符之间+1个递增 * 字符 。然后之前也是对着这个函数看了很久
错误的分析
第一次看到阴阳谜题时感觉应该挺简单,无非就是几个类似函数嵌套的迭代器嵌套罢了
正确的分析
- 时刻注意函数在调用时在内存中的地址,即所看到的函数名是不是真的所预想的那个函数
- 时刻注意传入的参数,因为参数 cb1 和 cb2 都是以函数为参数传入的,那么这个传入的函数名是不是真的所预想的那个函数
之前错误的分析就是太想当然,认为yang函数就一直是那个yang,认为yin函数就一直是那个yin(当然后面会发现,这个yin函数确实一直是yin函数),对于这个阴阳函数,最好的方式也是最容易理解的方式是通过断点辅助分析
def yin(cb1):
yield "@" //1 5 ...
def yang(cb2):
yield "*" //3 7 ...
yield from cb1(cb2) //4 8 ...
yield from yang(yang) //2 6 ...
ge = yin(yin)
for _ in range(100):
print(next(ge))
- 通过main函数的
yin(yin)
函数启动,启动yin
函数并且此时参数 cb1=yin,第一个next输出 @ - 启动
yang(yang)
- 此时参数cb2=yang,cb1=yin,并通过next输出 *
- 启动迭代器
yin(yang)
,❗❗❗此时的yang
是通过cb2传入,即第二步的yang
- 启动
yin(yang)
之后,参数cb1变为yang,并通过next输出 @ - 启动
yang(yang)
,此时的yang(yang)
均为新的函数,即在内存中会新开辟一个空间,如果有兴(wu)趣(liao)的的可以尝试输出这个yang和第2步的yang函数的id(yang)
地址,可以发现是不同的 - 此时参数cb1=yang(第2步生成的),参数cb2=yang(第6步生成的),并通过next输出 *
- 启动
yang(yang)
,这个yang
函数(第2步生成的)又回到了之前yin
函数的 “势力范围”,而此时此刻的参数 cb1=yin,cb2=yang(第6步生成的),所以此时又会重新启动yin(yang)
- …
再往下推几步就会发现,虽然第6步那个位置看起来在不停的循环启动 yang(yang)
,貌似进入了一个死循环,其实每一次重新进入 yang(yang)
的时候,yang
函数在不停的变化,不停的往回走,所以导致了
yield from cb1(cb2)
中的cb1不停的往上走,直到找到yin
函数重新开始…- 找到
yin
函数之后, 通过yield from cb1(cb2)
传给yin的cb2一直是最新生成的yang
这里我没有使用调用函数这个描述,因为都是生成器,用启动可能更加恰当,理解的时候用“调用”也是可以的
结束语
因为之前也是一直在研究但是没研究出来,具体的,阴阳谜题和我今天看的汉诺塔递归函数里面的传参,有着相似的诡异之处。另外,阴阳谜题是不是递归实现的目前我真的还看不出来,也许有递归的东西在里面,但是即使是递归实现的,感觉上只有递出去,并没有收回来,请指正~