aboutsummaryrefslogtreecommitdiffhomepage
path: root/README.md
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](assets/0.py.png)

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

![0.py output](assets/1.py.png)

これは `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
```

![107.py output](assets/107.py.png)

この実行結果は、「午」の字の形をしています。来年 2026 年は午年ですね!

また、Python のソースコードではなく PHP のソースコードとなっています。PHP のマスコットは馬......であればよかったのですが残念ながら象でした。

これを PHP インタプリタで実行すると、次のようになります。

![108.php output](assets/108.php.png)

これは、最初の `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 コードを保持し、それを加工なしで出力するようになっています。