Update 4 files

- /_data/other_repo_list.csv
- /_data/proxylist.yml
- /_posts/2024-04-06-old-pc.md
- /_posts/2025-09-01-quine.md
master
mayx 2025-09-17 12:55:54 +00:00
parent b4af6686ce
commit ebe45018f5
4 changed files with 10 additions and 5 deletions

View File

@ -10,6 +10,7 @@ https://git.pixie.town/mayx/mayx
https://cgit.tilde.town/~mayx/blog https://cgit.tilde.town/~mayx/blog
https://gitlab.haskell.org/mayx/mayx https://gitlab.haskell.org/mayx/mayx
https://git.gammaspectra.live/Mayx/blog https://git.gammaspectra.live/Mayx/blog
https://pagure.io/fork/mabbs/Blog
https://repo2.serv00.com/git/pub/Mayx/mayx/ https://repo2.serv00.com/git/pub/Mayx/mayx/
https://git.tea-assets.com/mayx/blog https://git.tea-assets.com/mayx/blog
https://repo.komhumana.org/mayx/blog https://repo.komhumana.org/mayx/blog

1 repo_url
10 https://cgit.tilde.town/~mayx/blog
11 https://gitlab.haskell.org/mayx/mayx
12 https://git.gammaspectra.live/Mayx/blog
13 https://pagure.io/fork/mabbs/Blog
14 https://repo2.serv00.com/git/pub/Mayx/mayx/
15 https://git.tea-assets.com/mayx/blog
16 https://repo.komhumana.org/mayx/blog

View File

@ -41,6 +41,7 @@ repos:
- https://git.disroot.org/mayx/mayx - https://git.disroot.org/mayx/mayx
- https://bitbucket.org/unmayx/mayx - https://bitbucket.org/unmayx/mayx
- https://sourcecraft.dev/mayx/mayx - https://sourcecraft.dev/mayx/mayx
- https://gitflic.ru/project/mayx/blog
- https://gitee.com/mabbs/mabbs - https://gitee.com/mabbs/mabbs
- https://cnb.cool/unmayx/mayx - https://cnb.cool/unmayx/mayx
- https://atomgit.com/mayx/blog - https://atomgit.com/mayx/blog

View File

@ -115,7 +115,7 @@ ossyNMMMNyMMhsssssssssssssshmmmhssssssso Memory: 8773MiB / 11928MiB
不过我按照官方文档上安装对于CentOS Stream 8来说有好多包不知道为什么似乎都没有比如libavformat-free-devel之类的我只好从网上找其他RedHat系列类似的包或者找替代品FFmpeg另外Darling需要Linux 5.0或者更高的内核CentOS的内核版本太低了所以我升到了主线版本的Linux也就是6.8的版本……最终花了一天的时间终于编译好了然而悲剧的是运行的时候报了非法指令“Illegal instruction (core dumped)”的错误。一般来说这个错误是新机器上编译的程序在旧机器运行才会报的错,可我是在同一台机器上编译的为什么会报这种错误呢?可能是因为代码里包含汇编语言的代码吧。我发了个[Issue](https://github.com/darlinghq/darling/issues/1497)问了一下作者,不过看起来他也不知道是什么问题…… 不过我按照官方文档上安装对于CentOS Stream 8来说有好多包不知道为什么似乎都没有比如libavformat-free-devel之类的我只好从网上找其他RedHat系列类似的包或者找替代品FFmpeg另外Darling需要Linux 5.0或者更高的内核CentOS的内核版本太低了所以我升到了主线版本的Linux也就是6.8的版本……最终花了一天的时间终于编译好了然而悲剧的是运行的时候报了非法指令“Illegal instruction (core dumped)”的错误。一般来说这个错误是新机器上编译的程序在旧机器运行才会报的错,可我是在同一台机器上编译的为什么会报这种错误呢?可能是因为代码里包含汇编语言的代码吧。我发了个[Issue](https://github.com/darlinghq/darling/issues/1497)问了一下作者,不过看起来他也不知道是什么问题……
对于这种问题我感觉也没什么好办法……可能这台机器真的就没办法了在第二台速龙641的电脑上试了一下也不行……不过后来我在第三台装有奔腾G3240的电脑上试着编译安装了一下结果可以运行。看来确实是奔腾E5300的问题。不过它俩到底差在哪里呢看介绍会发现奔腾G3240里包含了Intel® SSE4.1和Intel® SSE4.2的指令集扩展。那么对于没有这个指令集扩展的CPU就没办法了吗Intel官方给了一个解决方法是[Intel® SDE](https://www.intel.com/content/www/us/en/developer/articles/tool/software-development-emulator.html)可以在旧机器上模拟运行使用了最新指令集的程序甚至包括AVX512都可以模拟的出来但是我用这个东西运行Darling的时候还是报错了可能Darling需要用到内核的一些特性但是SDE不能模拟……这都没办法是不是就彻底没办法了呢 对于这种问题我感觉也没什么好办法……可能这台机器真的就没办法了在第二台速龙641的电脑上试了一下也不行……不过后来我在第三台装有奔腾G3240的电脑上试着编译安装了一下结果可以运行。看来确实是奔腾E5300的问题。不过它俩到底差在哪里呢看介绍会发现奔腾G3240里包含了Intel® SSE4.1和Intel® SSE4.2的指令集扩展。那么对于没有这个指令集扩展的CPU就没办法了吗Intel官方给了一个解决方法是[Intel® SDE](https://www.intel.com/content/www/us/en/developer/articles/tool/software-development-emulator.html)可以在旧机器上模拟运行使用了最新指令集的程序甚至包括AVX512都可以模拟的出来但是我用这个东西运行Darling的时候还是报错了可能Darling需要用到内核的一些特性但是SDE不能模拟……这都没办法是不是就彻底没办法了呢
在偶然的一次浏览中,我发现了一个神奇的东西,内核扩展[OPEMU](https://github.com/mirh/opemu-linux)它可以让不支持一些指令集扩展的CPU通过模拟的方式支持其实功能和SDE很像只是它是在内核中运行的我试着在第一台机器上编译安装了一下顺便一说如果是旧的5.x或者更早的Linux可以直接用这个仓库而更新的Linux比如6.x的需要用[PR](https://github.com/Spacefish/opemu-linux)中的这个仓库结果Darling真的可以运行了真是令人难以置信。 在偶然的一次浏览中,我发现了一个神奇的东西,内核扩展[OPEMU](https://github.com/mirh/opemu-linux)它可以让不支持一些指令集扩展的CPU通过模拟的方式支持其实功能和SDE很像只是它是在内核中运行的我试着在第一台机器上编译安装了一下顺便一说如果是旧的5.x或者更早的Linux可以直接用这个仓库而更新的Linux比如6.x的需要用[PR](https://github.com/Spacefish/opemu-linux)中的这个仓库结果Darling真的可以运行了真是令人难以置信。
安装成功之后我在网上找了个C语言的程序[endoh1](http://www.ioccc.org/2012/endoh1/hint.html)这个程序可以用文本模拟流体。我在我的MacBook上编译了试了一下运行没有问题当然直接编译的程序是ARM64的程序肯定不能在Darling里面运行于是我切换到x86_64模式下又编译了一次并且用`lipo`命令把两个程序合并到了一起然后把程序上传到第一台机器中使用Darling运行竟然可以正常运行看来那个内核扩展还不错啊Darling居然没有出问题。 安装成功之后我在网上找了个C语言的程序[endoh1](https://github.com/ioccc-src/winner/tree/master/2012/endoh1)这个程序可以用文本模拟流体。我在我的MacBook上编译了试了一下运行没有问题当然直接编译的程序是ARM64的程序肯定不能在Darling里面运行于是我切换到x86_64模式下又编译了一次并且用`lipo`命令把两个程序合并到了一起然后把程序上传到第一台机器中使用Darling运行竟然可以正常运行看来那个内核扩展还不错啊Darling居然没有出问题。
不过测试了一下可能还是有些地方有BUG比如用Git的时候会报错可能是和README中所说的CRC32表现有问题吧不过Darling好像可以直接运行Linux中的命令那我在用Git的时候调用Linux下的Git是不是也可以呢试了一下不太行因为执行Linux程序的时候不能用Darling中的目录结构不过我想装omz只需要/Users目录就够了我直接创建一个软链接把Darling的/Users目录映射到Linux的根目录就可以了吧试了一下还行可以正常运行虽然Homebrew不能安装有点可惜……不过Neofetch可以安装😆效果如下 不过测试了一下可能还是有些地方有BUG比如用Git的时候会报错可能是和README中所说的CRC32表现有问题吧不过Darling好像可以直接运行Linux中的命令那我在用Git的时候调用Linux下的Git是不是也可以呢试了一下不太行因为执行Linux程序的时候不能用Darling中的目录结构不过我想装omz只需要/Users目录就够了我直接创建一个软链接把Darling的/Users目录映射到Linux的根目录就可以了吧试了一下还行可以正常运行虽然Homebrew不能安装有点可惜……不过Neofetch可以安装😆效果如下
``` ```
'c. root@localhost.localdomain 'c. root@localhost.localdomain

View File

@ -15,7 +15,7 @@ tags: [压缩包, Quine, 自产生程序, Quine Relay]
关于原理方面,先看[Will Greenberg](https://github.com/wgreenberg)制作的一个[示例](https://wgreenberg.github.io/quine.zip/)在这里面有一个谜题使用“print M”原样输出接下来的M行输入内容和“repeat M N”从倒数第N行的输出内容开始重复M行这两个指令让最终执行的结果和输入的指令完全相同。这正是对DEFLATE压缩算法所使用的LZ77编码的一种简化模拟也就是说只要解决了这个问题就可以让压缩包在解压时原样输出自己了。 关于原理方面,先看[Will Greenberg](https://github.com/wgreenberg)制作的一个[示例](https://wgreenberg.github.io/quine.zip/)在这里面有一个谜题使用“print M”原样输出接下来的M行输入内容和“repeat M N”从倒数第N行的输出内容开始重复M行这两个指令让最终执行的结果和输入的指令完全相同。这正是对DEFLATE压缩算法所使用的LZ77编码的一种简化模拟也就是说只要解决了这个问题就可以让压缩包在解压时原样输出自己了。
这个问题看起来还挺复杂,不过在仓库的[Issues](https://github.com/wgreenberg/quine.zip/issues/1)就有人给出了几种解法(当然,这个题目解法不唯一),所以在理论上应该是可行的,那么接下来就需要研究压缩文件的格式来实现它了。 这个问题看起来还挺复杂,不过在仓库的[Issues](https://github.com/wgreenberg/quine.zip/issues/1)就有人给出了几种解法(当然,这个题目解法不唯一),所以在理论上应该是可行的,那么接下来就需要研究压缩文件的格式来实现它了。
## 实现ZIP Quine的探索 ## 实现ZIP Quine的探索
在[Russ Cox](https://swtch.com/~rsc/)写的[Zip Files All The Way Down](https://research.swtch.com/zip)文章中同样说明了这个原理而且给出了一个方案让上述这两个命令除了能够对命令本身的重复以外还可以添加一些额外数据这样才能做到构建一个压缩包文件。按照文章的描述如果用之前谜题的规则来说我们设头和尾的内容都是“print 0”那么Cox给出的方案如下 在[Russ Cox](https://swtch.com/~rsc/)写的[Zip Files All The Way Down](https://research.swtch.com/zip)文章中同样说明了这个原理而且给出了一个方案让上述这两个命令除了能够对命令本身的重复以外还可以添加一些额外数据这样才能做到构建一个压缩包文件。按照文章的描述如果用之前谜题的规则来说我们设头和尾的内容都是“print 0”那么Cox给出的方案如下
``` ```
print 0 print 0
print 2 print 2
@ -56,10 +56,10 @@ print 0
另外这个方案是针对使用基于LZ77与哈夫曼编码的DEFLATE压缩算法所以格式不重要。因此无论是ZIP还是GZIP以及TGZGZIP压缩后的TAR其实都是一样的因为他们都使用的是DEFLATE压缩算法。顺便一提[Matthew Barber](https://github.com/honno)写了一篇很棒的[文章](https://github.com/honno/gzip-quine)通过动画演示并详细讲解了如何实现一个简单的GZIP版ZIP Quine很值得一看。 另外这个方案是针对使用基于LZ77与哈夫曼编码的DEFLATE压缩算法所以格式不重要。因此无论是ZIP还是GZIP以及TGZGZIP压缩后的TAR其实都是一样的因为他们都使用的是DEFLATE压缩算法。顺便一提[Matthew Barber](https://github.com/honno)写了一篇很棒的[文章](https://github.com/honno/gzip-quine)通过动画演示并详细讲解了如何实现一个简单的GZIP版ZIP Quine很值得一看。
还有一点普通的TAR文件能否实现类似功能呢从原理来说估计不行因为TAR文件本身并没有压缩也不包含指令就单纯是一堆文件和元数据的拼接所以就做不到自包含了。 还有一点普通的TAR文件能否实现类似功能呢从原理来说估计不行因为TAR文件本身并没有压缩也不包含指令就单纯是一堆文件和元数据的拼接所以就做不到自包含了。
这么来看既然TGZ可以那是不是在我博客网站的压缩包里放一份和自己一模一样的压缩包是可行的很遗憾按照这个方法来看是做不到的由于压缩格式和编码的限制这个方案在实际实现时发现操作码需要是5个字节最后发现最多只有类似`repeat 64 64`这样的指令能够满足要求因此头尾区最多只能放64-5=59个字节的数据也就刚刚好能容纳压缩格式需要的内容几乎没法塞更多东西进去……显然这些限制导致这种方式对我来说意义就不大了何况作者的代码我也看不懂……而且还要考虑压缩包还存在校验用的CRC32需要找满足整个压缩包的CRC32正好在压缩包中的“不动点”。虽然从CRC32的原理来说应该有办法做到通过数学方式解决但这篇文章的作者因为解决了自包含的问题之后累了因此放弃继续研究选择直接暴力破解毕竟CRC32只有32位估计思考的时间都要比爆破的时间长吧😂。但如果是这样即使有方案能存下我博客的数据也不能在每次网站构建的时候都制作一次了…… 这么来看既然TGZ可以那是不是在我博客网站的压缩包里放一份和自己一模一样的压缩包是可行的很遗憾按照这个方法来看是做不到的由于压缩格式和编码的限制这个方案在实际实现时发现操作码需要是5个字节最后发现最多只有类似`repeat 64 64`这样的指令能够满足要求因此头尾区最多只能放64-5=59个字节的数据也就刚刚好能容纳压缩格式需要的内容几乎没法塞更多东西进去……显然这些限制导致这种方式对我来说意义就不大了何况作者的代码我也看不懂……而且还要考虑压缩包还存在校验用的CRC32需要找满足整个压缩包的CRC32正好在压缩包中的“不动点”。虽然从CRC32的原理来说应该有办法做到通过数学方式解决但这篇文章的作者因为解决了自包含的问题之后累了因此放弃继续研究选择直接暴力破解毕竟CRC32只有32位估计思考的时间都要比爆破的时间长吧😂。但如果是这样即使有方案能存下我博客的数据也不能在每次网站构建的时候都制作一次了……
虽然Russ Cox写的文章看起来做不到包含更多内容了但Erling Ellingsen制作的droste.zip却包含了一张图片说明并不是没办法加入更多数据只是没有找到正确的方法。在2024年[Ruben Van Mello](https://github.com/ruvmello)写了一篇论文[A Generator for Recursive Zip Files](https://www.mdpi.com/2076-3417/14/21/9797),在这篇论文里他不仅解决了包含的额外数据过少的问题,还编写了一个通用工具,能让普通人也能生成这样的压缩包,而且他还创新性的做了一种像衔尾蛇一样的双层嵌套循环压缩包,非常的有意思,所以接下来我打算试试他的方案。 虽然Russ Cox写的文章看起来做不到包含更多内容了但Erling Ellingsen制作的droste.zip却包含了一张图片说明并不是没办法加入更多数据只是没有找到正确的方法。在2024年[Ruben Van Mello](https://github.com/ruvmello)写了一篇论文[A Generator for Recursive Zip Files](https://www.mdpi.com/2076-3417/14/21/9797),在这篇论文里他不仅解决了包含的额外数据过少的问题,还编写了一个通用工具,能让普通人也能生成这样的压缩包,而且他还创新性的做了一种像衔尾蛇一样的双层嵌套循环压缩包,非常的有意思,所以接下来我打算试试他的方案。
在这篇论文中里面简述了之前Russ Cox写的内容也提到了59字节的限制于是作者对原有的结构进行了一些改动让操作码可以超出5字节的限制具体可以看论文的表6从而解决了只能包含59字节额外数据的限制。但由于DEFLATE压缩格式本身的约束16位存储块长度以及32KiB回溯窗口即使能够添加文件最多也只能额外容纳32763字节的数据其中包括压缩包所需的文件头……显然这点空间完全存不下我的博客😭看来我只能打消这个想法了。但既然都研究了半天也不一定要存我的博客嘛可以看看还有没有别的东西可以存在这之前先继续阅读论文看完再说吧。 在这篇论文中里面简述了之前Russ Cox写的内容也提到了59字节的限制于是作者对原有的结构进行了一些改动让操作码可以超出5字节的限制具体可以看论文的表6从而解决了只能包含59字节额外数据的限制。但由于DEFLATE压缩格式本身的约束16位存储块长度以及32KiB回溯窗口即使能够添加文件最多也只能额外容纳32763字节的数据其中包括压缩包所需的文件头……显然这点空间完全存不下我的博客😭看来我只能打消这个想法了。但既然都研究了半天也不一定要存我的博客嘛可以看看还有没有别的东西可以存在这之前先继续阅读论文看完再说吧。
## 制作一个嵌套循环的ZIP Quine ## 制作一个嵌套循环的ZIP Quine
在实现了常规的ZIP Quine之后接下来就是作者的创新点了如果光是解决存储限制这点创新点估计还不够发论文吧😂。作者接下来制作了一种循环压缩文件在压缩包内包含文件A和压缩包A而压缩包A中则包含文件B和最初的压缩包从而形成一个循环递归的结构。看论文的描述所说如果把外层的压缩包和内层的压缩包的开头和结尾按照一定的规则交替混合就可以看作是一个整体然后按照之前做ZIP Quine那样处理就可以……具体实现的细节得看论文的表10。只不过既然是把两个压缩包看作一个整体的话按照上面的限制自然每个压缩包能容纳的数据量就更小了每个最多只能容纳16376字节的数据…… 在实现了常规的ZIP Quine之后接下来就是作者的创新点了如果光是解决存储限制这点创新点估计还不够发论文吧😂。作者接下来制作了一种循环压缩文件在压缩包内包含文件A和压缩包A而压缩包A中则包含文件B和最初的压缩包从而形成一个循环递归的结构。看论文的描述所说如果把外层的压缩包和内层的压缩包的开头和结尾按照一定的规则交替混合就可以看作是一个整体然后按照之前做ZIP Quine那样处理就可以……具体实现的细节得看论文的表10。只不过既然是把两个压缩包看作一个整体的话按照上面的限制自然每个压缩包能容纳的数据量就更小了每个最多只能容纳16376字节的数据……
另外既然这里面有两个压缩包那么每个压缩包还有自己的CRC32校验和理论上如果要爆破的话计算难度得是原来的平方这样难度就太大了。不过作者发现如果把数据的CRC32值取反即与“0xFFFFFFFF”取异或然后和原始数据拼到一起整个数据的CRC32校验和就会被重置为一个固定的值“0xFFFFFFFF”看起来挺有意思正常的哈希算法可没有这种特性。因此原本计算难度很大的爆破计算现在就可以和之前一样了……话说为什么不让两层的CRC32都这样计算包括之前单层的ZIP Quine这样就不需要爆破了……貌似是因为在普通的ZIP Quine中满足条件的CRC32需要出现两次所以不能用这个方案吧 另外既然这里面有两个压缩包那么每个压缩包还有自己的CRC32校验和理论上如果要爆破的话计算难度得是原来的平方这样难度就太大了。不过作者发现如果把数据的CRC32值取反即与“0xFFFFFFFF”取异或然后和原始数据拼到一起整个数据的CRC32校验和就会被重置为一个固定的值“0xFFFFFFFF”看起来挺有意思正常的哈希算法可没有这种特性。因此原本计算难度很大的爆破计算现在就可以和之前一样了……话说为什么不让两层的CRC32都这样计算包括之前单层的ZIP Quine这样就不需要爆破了……貌似是因为在普通的ZIP Quine中满足条件的CRC32需要出现两次所以不能用这个方案吧
现在所有的理论都足够了我需要挑一个文件来做这样嵌套循环的ZIP Quine既然博客的大小不可以……要不然我就用我写过的第一个大项目——[Mabbs](https://github.com/Mabbs/Mabbs.Project)吧这个项目的主程序是22KiB看起来似乎超出了嵌套循环ZIP Quine的限制其实没有它的限制指的是压缩后的大小我这个程序压缩之后是8KiB左右所以完全没问题。 现在所有的理论都足够了我需要挑一个文件来做这样嵌套循环的ZIP Quine既然博客的大小不可以……要不然我就用我写过的第一个大项目——[Mabbs](https://github.com/Mabbs/Mabbs.Project)吧这个项目的主程序是22KiB看起来似乎超出了嵌套循环ZIP Quine的限制其实没有它的限制指的是压缩后的大小我这个程序压缩之后是8KiB左右所以完全没问题。
接下来就该使用论文中提到的生成工具:[zip-quine-generator](https://github.com/ruvmello/zip-quine-generator)这是一个Kotlin编写的程序从发布中可以下载预构建的程序接下来只要按照README中的描述使用“`--loop`”参数就可以用这个程序创建嵌套循环的ZIP Quine了。不过它原本的代码不能修改里面生成的压缩包的名字另外[压缩后的文件属性是隐藏文件](https://github.com/ruvmello/zip-quine-generator/blob/3b8cf977e7a93bb956ad966d5e3b4d503f410529/src/main/kotlin/zip/ZIPArchiver.kt#L845),还有[生成的压缩包中文件的创建时间总是当前时间](https://github.com/ruvmello/zip-quine-generator/blob/3b8cf977e7a93bb956ad966d5e3b4d503f410529/src/main/kotlin/zip/ZIPArchiver.kt#L29),以及[给文件内填充额外数据的代码里面填的是作者的声明](https://github.com/ruvmello/zip-quine-generator/blob/3b8cf977e7a93bb956ad966d5e3b4d503f410529/src/main/kotlin/zip/ZIPArchiver.kt#L30)表示文件是由他论文的所写的生成器生成的……这些情况让我感觉有点不爽还是希望这些部分能自定义一下所以我就小改了一下他的代码。顺便一说Kotlin编译起来还挺简单直接一句`kotlinc src/main/kotlin -include-runtime -d output.jar`就可以了也不需要折腾Maven之类乱七八糟的东西。最终我修改并编译完程序之后就把文件丢到服务器上开始给我爆破CRC32了花了10个小时就算出来了倒是比想象中快😂。 接下来就该使用论文中提到的生成工具:[zip-quine-generator](https://github.com/ruvmello/zip-quine-generator)这是一个Kotlin编写的程序从发布中可以下载预构建的程序接下来只要按照README中的描述使用“`--loop`”参数就可以用这个程序创建嵌套循环的ZIP Quine了。不过它原本的代码不能修改里面生成的压缩包的名字另外[压缩后的文件属性是隐藏文件](https://github.com/ruvmello/zip-quine-generator/blob/3b8cf977e7a93bb956ad966d5e3b4d503f410529/src/main/kotlin/zip/ZIPArchiver.kt#L845),还有[生成的压缩包中文件的创建时间总是当前时间](https://github.com/ruvmello/zip-quine-generator/blob/3b8cf977e7a93bb956ad966d5e3b4d503f410529/src/main/kotlin/zip/ZIPArchiver.kt#L29),以及[给文件内填充额外数据的代码里面填的是作者的声明](https://github.com/ruvmello/zip-quine-generator/blob/3b8cf977e7a93bb956ad966d5e3b4d503f410529/src/main/kotlin/zip/ZIPArchiver.kt#L30)表示文件是由他论文的所写的生成器生成的……这些情况让我感觉有点不爽还是希望这些部分能自定义一下所以我就小改了一下他的代码。顺便一说Kotlin编译起来还挺简单直接一句`kotlinc src/main/kotlin -include-runtime -d output.jar`就可以了也不需要折腾Maven之类乱七八糟的东西。最终我修改并编译完程序之后就把文件丢到服务器上开始给我爆破CRC32了花了10个小时就算出来了倒是比想象中快😂。
@ -77,7 +77,7 @@ c = 'c = %r; print(c %% c)'; print(c % c)
``` ```
D'<;_98=6Z43Wxx/.R?Pa D'<;_98=6Z43Wxx/.R?Pa
``` ```
代码就像加了密似的顺便一说这个执行结果是“Mayx”关于Malbolge的具体细节可以看它的[规范](http://www.lscheffer.com/malbolge_spec.html),另外虽然这个语言写起来很复杂,但还是有人能用它编出程序的,甚至还有人用[Malbolge Unshackled](https://esolangs.org/wiki/Malbolge_Unshackled)Malbolge不限内存的变种写过[Lisp解释器](https://github.com/iczelia/malbolge-lisp),实在是恐怖如斯😨。 代码就像加了密似的,顺便一说这个执行的输出结果是“Mayx”关于Malbolge的具体细节可以看它的[规范](http://www.lscheffer.com/malbolge_spec.html),另外虽然这个语言写起来很复杂,但还是有人能用它编出程序的,甚至还有人用[Malbolge Unshackled](https://esolangs.org/wiki/Malbolge_Unshackled)Malbolge不限内存的变种写过[Lisp解释器](https://github.com/iczelia/malbolge-lisp),实在是恐怖如斯😨。
## 只能Quine的语言 ## 只能Quine的语言
其实想要做出Quine还有一种更加无聊的方案那就是设计一种只能Quine的语言🤣。根据Quine的定义代码输出的结果就是它本身……所以我们可以把任何内容都看作代码然后这种语言的行为就是输出所有代码……听起来是不是有点无聊但是想想看如果把Linux中的cat命令当作解释器就可以实现这种语言了比如 其实想要做出Quine还有一种更加无聊的方案那就是设计一种只能Quine的语言🤣。根据Quine的定义代码输出的结果就是它本身……所以我们可以把任何内容都看作代码然后这种语言的行为就是输出所有代码……听起来是不是有点无聊但是想想看如果把Linux中的cat命令当作解释器就可以实现这种语言了比如
``` ```
@ -88,6 +88,9 @@ Hello, world!
## Quine Relay的探索 ## Quine Relay的探索
还有一个更加复杂的Quine变种是“Quine接力”Quine Relay即一个程序输出另一个程序的源代码另一个程序又输出下一个程序的源代码最后回到原始程序就和之前所说的嵌套循环ZIP Quine有点类似。最著名的例子是[Yusuke Endoh](https://github.com/mame)(这位还是[IOCCC](https://www.ioccc.org/)的冠军之一)创建的[quine-relay](https://github.com/mame/quine-relay)项目它包含了128种编程语言的循环。 还有一个更加复杂的Quine变种是“Quine接力”Quine Relay即一个程序输出另一个程序的源代码另一个程序又输出下一个程序的源代码最后回到原始程序就和之前所说的嵌套循环ZIP Quine有点类似。最著名的例子是[Yusuke Endoh](https://github.com/mame)(这位还是[IOCCC](https://www.ioccc.org/)的冠军之一)创建的[quine-relay](https://github.com/mame/quine-relay)项目它包含了128种编程语言的循环。
这种程序写起来会更复杂一些不过原理都差不多通常除了当前运行的部分是可执行代码外其他的代码都需要以额外包含的数据形式如字符串存储在变量中。如果想自己做个类似简单的Quine Relay除了去看[维基百科](https://en.wikipedia.org/wiki/Quine_(computing)#Ouroboros_programs)之外,前段时间我还看到过一个不错的[文章](https://blog.mistivia.com/posts/2024-09-21-quine/)里面就讲了如何用“笨办法”编写Quine和Quine Relay通过把变量中的内容编码为16进制来避免不同语言可能存在的特殊字符转译问题思路不错对于理解如何编写这类程序的问题很有帮助。当然这只是个**简单**的方案,仅适用于一些常规的编程语言,像上面那个[quine-relay](https://github.com/mame/quine-relay)项目中甚至还包含Brainfuck之类的esolang这种估计得要想办法让相对高级一些的语言通过“生成”的方式得到输出下一种代码的代码而不是简单的赋值了所以只靠这点知识想去完全理解大佬的作品还是想多了😆。 这种程序写起来会更复杂一些不过原理都差不多通常除了当前运行的部分是可执行代码外其他的代码都需要以额外包含的数据形式如字符串存储在变量中。如果想自己做个类似简单的Quine Relay除了去看[维基百科](https://en.wikipedia.org/wiki/Quine_(computing)#Ouroboros_programs)之外,前段时间我还看到过一个不错的[文章](https://blog.mistivia.com/posts/2024-09-21-quine/)里面就讲了如何用“笨办法”编写Quine和Quine Relay通过把变量中的内容编码为16进制来避免不同语言可能存在的特殊字符转译问题思路不错对于理解如何编写这类程序的问题很有帮助。当然这只是个**简单**的方案,仅适用于一些常规的编程语言,像上面那个[quine-relay](https://github.com/mame/quine-relay)项目中甚至还包含Brainfuck之类的esolang这种估计得要想办法让相对高级一些的语言通过“生成”的方式得到输出下一种代码的代码而不是简单的赋值了所以只靠这点知识想去完全理解大佬的作品还是想多了😆。
## Polyglot Quine的探索
除了Quine Relay之外还有一种很复杂的Quine叫做[Polyglot](https://en.wikipedia.org/wiki/Polyglot_(computing)) Quine与Quine Relay需要在程序执行后才能切换到其他语言接力不同Polyglot Quine的源代码本身即可同时属于多种语言而且用这些语言的解释器每个执行后的输出全都一样都与源代码完全一致。由于不同的编程语言的格式既有些相同之处也有很多不同之处所以让同一份代码表示不同语言就会很容易产生歧义这时候就只能想办法通过一些特别的方式比如将可能会对当前语言产生干扰的代码看作是注释的方式来规避语言之间的差异。
Quine本身就已经很困难了再加上这些限制就变得更加复杂了所以制作Polyglot Quine的编程语言基本上都得精挑细选而且通常只有两种语言比如[这段代码](https://github.com/TrAyZeN/polyglot-quine/blob/master/main.c)就是C和Python的Polyglot Quine它巧妙利用了C预处理器指令在Python中可视为注释的特性使两种语言互不干扰非常有趣。更令人惊叹的则是[PyZipQuine](https://github.com/d0sboots/PyZipQuine)项目它既可以被当作压缩包也可以作为Python2.7代码而且二者都是Quine实在令人赞叹。
# 感想 # 感想
虽然这次探索最终没能完成让包含博客所有内容的压缩包自包含但是在探索的过程中我还是收获了不少尤其是Ruben Van Mello制作的ZIP Quine生成工具实在是太棒了。很久以前我见到droste.zip这个压缩包的时候就想整一个属于自己的ZIP Quine现在我不仅用那个生成工具做了一个还是对我来说很有意义的第一个项目——Mabbs而且更关键的还是生成的是比普通的ZIP Quine更高级的嵌套循环ZIP Quine也算是圆了小时候的心愿了。 虽然这次探索最终没能完成让包含博客所有内容的压缩包自包含但是在探索的过程中我还是收获了不少尤其是Ruben Van Mello制作的ZIP Quine生成工具实在是太棒了。很久以前我见到droste.zip这个压缩包的时候就想整一个属于自己的ZIP Quine现在我不仅用那个生成工具做了一个还是对我来说很有意义的第一个项目——Mabbs而且更关键的还是生成的是比普通的ZIP Quine更高级的嵌套循环ZIP Quine也算是圆了小时候的心愿了。