Posted at

D言語くんのAAになっているD言語で書かれたquine

More than 3 years have passed since last update.


D言語くんのAAになっているD言語で書かれたquine


  1. D言語でただのquine書いて

  2. D言語くんのAA書いて

  3. フォーマッタ書いて

  4. 合成しただけ


普通のquineを書く

まず,標準出力に自分自身が現れて欲しいので,

const(char[])s="import std.stdio;void main(){writeln(s);}";mixin(s);

みたいなのを書く.出力↓

import std.stdio;void main(){writeln(s);}

惜しい.でもちょっと変えればquineになることにすぐに気づく.

改良後

const(char[])s=q"{import std.stdio;void main(){writeln(`const(char[])s=q"{`~s~`}";mixin(s);`);}}";mixin(s);

writeln(s)のsにconst(char[])s=q"っていう文字列と}";mixin(s);っていう文字列をくっつけた.

この文字列中にはダブルクオート(")が入っているのでネストできるq"{}"という生文字列リテラルを使う.

これでquineが出来た.


D言語くんのAA

描くだけ.結構文字数が必要だとわかっているので大きめにする.

                                 ###                            

###### #######
############ #########
########################
#########################
#########################
###########################
########################### ##
## ####################### ##
## ########### ######## ##
## ######## ##
## ##
## ##
## ##
## ##
## ##
## ##
## ######### ##
## ########### ######## ##
## #### ### ##
## #### # ######## ##### ## ##
## # # # # # # # ##
### # # ##### # ##### # ##
### # # ########## ####### # ##
### # # ######## # ####### ###
# ## # # ########### ######## ##
# ## # # ####### # ######### #
# ## # ######### # ####### # #
# # # ####### # #
# # # # # #
# # # # # #
# # # # # #
# # # # # #
# # # # # #
# # # # # #
# # # # # #
# # # # # #
# # # ## # #
# # # ### # #
# # # ### ## #
# # # ####### #
# # ########## #
# # ##
# # #####
# # #### #
# ## ########## #
# ##################### ##
### ## ####
# ## ######
### ## ######### ##
################## ##
## ##
## ##
## ##
## ##
## ##
## ##
## ##
## ## #####
##### ## ## ###########
######### ## ################
############# ###############
############ ############
######### #####


フォーマッタを書く

文字列を受け取ってこの形に出力するコードを書く.この画像をそのままデータとして持つと,quineは(コード+データ)の形にしなければならないのにデータだけですでにquineコードの容量をオーバーしてしまうので,なんとか圧縮する方法を考える.

このAAは56x64なので,スペースを0,#を1としてint[]rの配列変数に格納する.

文字列変数の名前をsとするとフォーマッタは

int c=0;

foreach(i,n;r)n?write(s[c++]):write(" "),(i+1)%56||writeln("");

と書ける.

実際にrに[0,…,0,1,…,1]や,[0,1,0,1,…]などと入れて確かめるとうまくいくことがわかる.

さて,肝心の圧縮だが,Dの標準ライブラリにzlibがあるので,これを使えばうまく圧縮できるのではないだろうか.

圧縮されているデータをuncompressしたものがint[]rとなれば良いので,とりあえずwriteln(compress(r,9).length)とする.

324バイトの配列になったので大幅に圧縮できたが,中身はただのバイト列なので,印字可能文字にするためbase64エンコードをする.

writeln(Base64.encode(compress(r,9)).length)とすると432文字とある.

上のAAの#の文字数は843文字なので半分以上base64に持って行かれることになるが,まあ残り400文字あればquineできるだろう.

ちなみに,Base64エンコードした文字列は以下になる.

eNq9l9sSgyAMRLP//9Od1nLZ3HFqfVAGcwiGDUSR/AIg7Qufa7XQZr7mq3WC0RD3sL9zKZlRCfh+FTsLQVqxy5jmGICjO/4eF+yIwrHpydBYddWr7PqiJ8uzXLmFbdZBeKvlIMfW3OFJPpXoeazBXg/PwdZe5AVOk3lbjf0+n8PZZgr2Au1SKVlxKoEY3KcL4rZEMnshxWeuiRQ5qIU2klZ7gFUAczLDwHZz75WAU31QenGN9KKpUXmJcocPcN5E/8yFmMCL9Zm7M85btZKDP03DwewQrrskLPYMa7lzNrNELcnhDEnToXWs9DDrv+ZUBvJOibi8iEVUVyzuuEUo6tDaIzU6cOCd2a2CR4pPu1midMCiljrEKrCoFX/u7/b3PVQruqqNy1pVPwdlVZZPTukUV/xFdsWbUf4XUb3irhfw7ANM


これらを合成する

最初のquineの

const(char[])s=q"{import std.stdio;void main(){writeln(`const(char[])s=q"{`~s~`}";mixin(s);`);}}";mixin(s);

writelnの部分をフォーマッタに変更すれば,いい感じになる.

ここで注意するのが,実際にmixinに渡される文字列はAAの形をしているので,適当にスペース,改行を取り除き,int cなど,スペースが必要な場所は@に置換することで対応する.

translateという関数が有ったのでこれを使い,sの中身もAAの形の一部なので,

char[]t=removechars(`///import@std.///string;const(char[])s=q"{`~s~`}";;;;mixin(translate(s,makeTrans([64],[32]),[32,10]));////`,[32,10]);`

としてスペース,改行を取り除いた後,char[]tに代入し,実際にフォーマッタに渡される文字列はこれを使うようにする.

また,生文字列以外の部分,例えば,最初のimport std.string;や最後のmixin()は識別子が分断されると構文エラーになってしまうので,ちょこちょこ適当な文字を入れてうまく識別子がそのままになるよう工夫する.

import std.string;const(char[])s=q"{

import@std.zlib,std.base64,std.stdio,std.string;
void@main(){
byte[]u=cast(byte[])Base64.decode("eNq9l9sSgyAMRLP//9Od1nLZ3HFqfVAGcwiGDUSR/AIg7Qufa7XQZr7mq3WC0RD3sL9zKZlRCfh+FTsLQVqxy5jmGICjO/4eF+yIwrHpydBYddWr7PqiJ8uzXLmFbdZBeKvlIMfW3OFJPpXoeazBXg/PwdZe5AVOk3lbjf0+n8PZZgr2Au1SKVlxKoEY3KcL4rZEMnshxWeuiRQ5qIU2klZ7gFUAczLDwHZz75WAU31QenGN9KKpUXmJcocPcN5E/8yFmMCL9Zm7M85btZKDP03DwewQrrskLPYMa7lzNrNELcnhDEnToXWs9DDrv+ZUBvJOibi8iEVUVyzuuEUo6tDaIzU6cOCd2a2CR4pPu1midMCiljrEKrCoFX/u7/b3PVQruqqNy1pVPwdlVZZPTukUV/xFdsWbUf4XUb3irhfw7ANM").uncompress,r;
char[]t=removechars(`///import@std.///string;const(char[])s=q"{`~s~`}";;;;mixin(translate(s,makeTrans([64],[32]),[32,10]));////`,[32,10]);
int@c;
foreach(i,n;u)write(n?t[c++]:'@'),(i+1)%56||"".writeln;
}
}"
;
mixin(translate(s,makeTrans([64],[32]),[32,10]));

これを実行すると

                                ///                     

import std.///
string;const (char[])s
=q"{import@std.zlib,std.
base64,std.stdio,std.stri
ng;void@main(){byte[]u=ca
st(byte[])Base64.decode("eN
q9l9sSgyAMRLP//9Od1nLZ3HFqf VA
Gc wiGDUSR/AIg7Qufa7XQZr7m q3
WC 0RD3sL9zKZl RCfh+FTs LQ
Vq xy5jmGIC jO
/4 eF
+y Iw
rH py
dB Yd
dW r7
Pq iJ
8u zXLmFbdZB eK
vl IMfW3OFJPpX oeazBXg/ Pw
dZ e5AV Ok3 lb
jf 0+n8 P ZZgr2Au1 SKVlx Ko EY
3K c L 4 r Z E M ns
hxW e u iRQ5q I U2klZ 7 gF
UAc z L DwHZz75WAU 31QenGN 9 KK
pUX m J cocPcN5E / 8yFmMCL 9Zm
7 M8 5 b tZKDP03Dwew QrrskLPY Ma
7 lz N r NELcnhD E nToXWs9DD r
v +Z U BvJOibi8i E VUVyzuu E U
o 6 t DaIzU6c O C
d 2 a 2 C R
4 p P u 1 m
i d M C i l
j r E K r C
o F X / u 7
/ b 3 P V Q
r u q q N y
1 p V P w d
l V Z ZP T u
k U V /xF d s
W b U f4X Ub 3
i r h fw7ANM" )
. u ncompress, r
; c ha
r [ ]t=re
m o vech a
r s( `///import @
s td.///string;const(ch ar
[]) s= q"{`
~ s~ `}";;;
;mi xi n(transla te
(s,makeTrans([64], [3
2] ),
[3 2,
10 ])
); //
// `,
[3 2,
10 ])
;i nt @c;fo
reach (i ,n ;u)write(n?
t[c++]:'@ ') ,(i+1)%56||"".wr
iteln;}}"
;;;; mixin(translate
(s,makeTrans ([64],[32]),
[32,10])) ;////

となり,確かにD言語くんのAAのquineとなっている.

これを作るにあたり,あなたの知らない超絶技巧プログラミングの世界を参考にしました.皆さん買いましょう.