blob: 7e8e3916663f2a456a150e176ad393ee6aabde45 (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
|
# これは何?
2025-12-20 に開催された [PHPer大忘LT会 2025](https://connpass.com/event/372021/) の発表資料・ソースコードです。
```
$ python 0.py > 1.py
$ python 1.py > 2.py
$ python 2.py > 3.py
...
$ python 107.py > 108.php
$ php 108.php > 0-2.py
$ diff 0.py 0-2.py
```
変則 Quine であり、最初の `0.py` は `gen.py` によって生成されます (`make` で出力可能)。
# 基本動作
`0.py` は「巳」の字の形をしています。これは、2025 年が巳年であることにちなんだものです。Python を使っているのも同様の理由です。

`0.py` を Python で実行すると、次のような出力が得られます。

これは `0.py` と同じく Python コードであり、その大部分が `0.py` と共通しています。
この出力を `1.py` として保存し、実行結果を `2.py` として保存するという動作を繰り返すと、108 回目に異なる動作となります (「108」という数字は、除夜の鐘から取ったものです)。
```
$ python 0.py > 1.py
$ python 1.py > 2.py
$ python 2.py > 3.py
...
$ python 107.py
```

この実行結果は、「午」の字の形をしています。来年 2026 年は午年ですね!
また、Python のソースコードではなく PHP のソースコードとなっています。PHP のマスコットは馬......であればよかったのですが残念ながら象でした。
これを PHP インタプリタで実行すると、次のようになります。

これは、最初の `0.py` と完全に一致します。以降は同じ挙動を繰り返します。
# 解説
基本となるのは、mame 氏による「Quine リレー」の記事 (https://mametter.hatenablog.com/entry/20130715/p1) にて説明されているテクニックです。
> 普通の Quine は、こんな感じの構造です。
>
> ```
> s = (自分自身のソースコード文字列を得る)
> puts s
> ```
>
> 一方、例えば何かを出力する Perl プログラム、を出力する Ruby プログラムはこんな感じです。
>
> ```
> puts "print(\"...\");"
> ```
>
> これと Quine を組み合わせれば、Ruby と Perl を行き来する multi-Quine のできあがり。
>
> ```
> s = (自分自身のソースコード文字列を得る)
> puts "print(\"#{ s }\");"
> ```
実装に用いた言語こそ異なりますが、基本となるのはこのアイデアです。
ここに、「巳」「午」の字形を出力したり、108 回のカウントをしたりする機構を加えると、今回のソースコードができます。
`0.py` は `gen.py` というプログラムによって生成しており、その `template1` 変数に記述されたソースコードが動作の基盤となるものです。
その大部分を占めている出力の形を整えるコード (`fmt`) を除くと、次のようになっています。
```python
from base64 import b64encode
from zlib import compress, decompress
j = b'107' # 108 回のカウント
s = b"@@" # この変数にソースコードが埋め込まれる
z = decompress(b64decode(s)).replace(b"j = b'%s'" % j, b"j = b'%03d'" % ((int(j)+1) % 108))
t = z.replace(b"@"+b"@", b64encode(compress(z)))
u = b64encode(t)
def fmt(u, f, php): # 整形
...
F1 = ["巳の字形データ"]
F2 = ["午の字形データ"]
v = fmt(u, F1, False) # Python で「巳」に整形
if j == b"107":
# 108 回目。PHP で「午」に整形
print(fmt(b64encode(compress(v)), F2, True).decode("utf-8"))
else:
print(v.decode("utf-8"))
```
言語間での文字列エスケープを簡単にするため、Base64 エンコードを用いています。これによってソースコード中のクォートやバックスラッシュが消えるので、ソースコードを内部に埋め込む際のエスケープについて考える必要がなくなります。
また、PHP コード側で整形処理を書く手間を減らすため、ソースコードの整形処理はすべて Python 側でおこなうようにしています。「午」の PHP コードは、内部に「巳」の形へ整形済みの Python コードを保持し、それを加工なしで出力するようになっています。
|