[python] 根据递归定理,打印出自身的程序
引言
fzu可计算性理论作业
来源于《计算性理论导引》(第三版、机械工业出版社)一书中p170练习题6.1
该书中递归定理部分在p157页有介绍,但其本质应该是依靠p155-156的自引用定理
思路就是先做自引用定理的SELF,那么递归定理也就同理了
任务
练习6.1:用一个实际的程序设计语言(或它的一个合理的近似)写一个本质上反映递归定理的例子,它将其自身打印出来)
自引用定理·图灵机SELF构造·人话
SELF分为A、B两部分
A运行完能打印出B的描述
B运行,根据A打印出的、自己的描述再构造出A的描述
B将两部分描述合并为SELF的描述并打印出来
思路
上课看到时第一反应就是利用文件读写,读取自身并再写出来,很简单嘛
实际也确实如此,以python为例,文件名为tm.py:
with open('.\tm.py', 'r', encoding='utf-8') as f:
print(f.read())
完美
但隐约觉得这样似乎并不符合要求
决定照着自引用定理先实现A、B
想法很单纯,
A.py里用个变量存储B的代码,然后将其写入文件中,即为打印了B的描述
然后B.py里读入A写入的文件,再加上A里的文件读写代码就组装成了A的代码,将其写入文件中,即为打印了A的描述
A.py
descriptor_b = f'''with open('[B]', 'r') as f:
descriptor_b = f.read()
with open('[A]', 'w') as f:
f.write(f"descriptor_b = f'''{descriptor_b}'''\nwith open('[B]', 'w') as f:\nf.write(descriptor_b)")
'''
with open('[B]', 'w') as f:
f.write(descriptor_b)
B.py
with open('[B]', 'r') as f:
descriptor_b = f.read()
with open('[A]', 'w') as f:
f.write(f"descriptor_b = f'''{descriptor_b}'''\nwith open('[B]', 'w') as f:\nf.write(descriptor_b)")
然而这样走不通,主要是A.py里三引号'''嵌套引起冲突,这是由于B代码里要组装出A,descriptor_B这个字符串比含有A,也就是自己套了自己,还必须得这样套
当然,换引号也没用
想了很多办法,比如不显式写出''',用char(39)*3在里面计算出来:
A.py
descriptor_b = f'''with open('[B]', 'r') as f:
descriptor_b = f.read()
with open('[A]', 'w') as f:
descriptor_b = descriptor_b.replace(
chr(39)*3, "{{chr(39)*3}}"
).replace(
"{chr(39)*3}", "{{{{chr(39)*3}}}}", 1
).replace(
"{{descriptor_b}}", "{{{{descriptor_b}}}}", 1
).replace(r"\\n", r"\\\\n")
f.write(f"descriptor_b = f{chr(39)*3}{{descriptor_b}}{chr(39)*3}\\nwith open('[B]', 'w') as f:\\n f.write(descriptor_b)\\n")
'''
with open('[B]', 'w') as f:
f.write(descriptor_b)
可惜这样还是不行,解决了'''又会因为f里面的大括号{}会被转义而不得不陷入了新的套娃
总是差一点点,一度想要放弃
后来洗了个澡,想到书里这个图灵机的描述也没说就是原原本本对应到代码上
于是打算把B的代码整个编码成字节串再写进A里面,A在写文件前再把它解码为字符串就好了
A.py
descriptor_b = b'with open(\'[B]\', \'r\') as f:\n descriptor_b = f.read()\nwith open(\'[A]\', \'w\') as f:\n db1, db2 = descriptor_b.rsplit(\'\\n\', 1)\n descriptor_b = db1 + \'\\n\' + db2.replace(\'\\n\', \'#\')\n descriptor_b = descriptor_b.encode()\n f.write(f"descriptor_b = {descriptor_b}\\nwith open(\'[B]\', \'w\') as f:\\n db1, db2 = descriptor_b.decode().rsplit(\'\\\\n\', 1)\\n descriptor_b = db1 + \'\\\\n\' + db2.replace(\'#\', r\'\\\\n\')\\n f.write(descriptor_b)\\n")\n'
with open('[B]', 'w') as f:
db1, db2 = descriptor_b.decode().rsplit('\n', 1)
descriptor_b = db1 + '\n' + db2.replace('#', r'\n')
f.write(descriptor_b)
B.py
with open('[B]', 'r') as f:
descriptor_b = f.read()
with open('[A]', 'w') as f:
db1, db2 = descriptor_b.rsplit('\n', 1)
descriptor_b = db1 + '\n' + db2.replace('\n', '#')
descriptor_b = descriptor_b.encode()
f.write(f"descriptor_b = {descriptor_b}\nwith open('[B]', 'w') as f:\n db1, db2 = descriptor_b.decode().rsplit('\\n', 1)\n descriptor_b = db1 + '\\n' + db2.replace('#', r'\\n')\n f.write(descriptor_b)\n")
注意到里面用到 rsplit
与 replace
是因为编码成字节串后原本字符间的换行符就会被显式转换成'\n',就会跟B里拼装A时使用的'\n'混淆,
于是先将B里需要的这部分换成别的,如'#'再去编码,最后再换回来即可
效果很好
*乎上看到一个使用 Kleene 递归定理的能输出自己的 Python 程序
没有仔细看,大概跟网上另一个使用C里面pritf实现用ascii码代替引号有相通之处
(lambda : (lambda y: (lambda x: (print('('+x+')()'), x)[-1])('lambda : (%s)(%r)' % (y, y)))("lambda y: (lambda x: (print('('+x+')()'), x)[-1])('lambda : (%s)(%r)' % (y, y))"))()
但好像也不符合本题所需递归定理的思想,权作补充
而且既然都单文件了,我还是直接文件读写吧...
本文作者:NoNoe
本文链接:https://www.cnblogs.com/Stareven233/p/14729561.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步