『残像に出力を』 — 文字を失っても"Hello World"を諦めないTypeScriptファルス
※この記事は特定の文字の使用を禁止してHello Worldをプログラムで出力するものです(例えばOを使わずになど)
※ソースコードと説明だけ見たい方はストーリーを飛ばしてください(驚くほどしょうもないので)
ストーリー
始まり
console.log('Hello World')
女:「また、それ? 'Hello World'ってやつ。」
男:「……ああ、これは挨拶の儀式なんだ。忘れちゃいけない。毎朝、画面に向かって、世界に語りかける。習慣ってやつさ。」
女:「あなたのそれ、びっくりマーク付いてないじゃない。」
男:「私の言葉には、マークなんてつかないんだ。」
女:「ずいぶん地味な世界観ね……。」
(ぽとん、と音がして、何かがディスプレイから剥がれ落ちた)
男:「.....d?」
女:「なにしてるの?」
男:「dが落ちている」
女:「d?」
男:「ああ、dだ。まぎれもなく。」
console.log('Hello Worl')
男:「……君が盗んだのか、dを?」
女:「盗む理由がある?」
男:「じゃあ、誰が? 私のdを。Consoleの中から抜き取ったのは?」
女:「知らないわ。」
男は怪訝な顔をしながら目の前のキーボードに手を置きdを押す
男:「やはり、盗まれている……。」
dの喪失
次の朝
女:「今日も?」
男:「あぁ、でもdが盗まれたままだ。」
女:「ふうん、それで?」
男:「Hello Worldが完成しない」
女:「dを出力するのにdなんて必要ないでしょ?」
console.log('Hello Worl\u0064');
女:「姿が変わっても、意味は変わらない。あなたの問題は、中身より見た目を信じること。」
男:「それは……コードにも言えることか?」
女:「文字なんてね、肉体にすぎないの。魂が通じれば、意味は生きるわ。」
喪失の連鎖
次の朝
女:「で?」
男:「あぁ」
女:「次は?」
男:「oだ」
女:「それは困ることなの?」
男:「もちろんだ。oがなければconsole.logが呼び出せない。世界への語りかけが封じられる。」
男:「口をふさがれてしゃべれる人はいない」
女:「じゃあ、口の代わりに手を使えば? 点字でもいい。モールス信号もあるわ」
男:「君は……冷たいな。」
女:「あなたは名前に呪われてるわね。 'console'と 'log' に、そう呼ばなきゃ通じないと思い込んでる。」
(gl\u006FbalThis as any)["c\u006Fns\u006Fle"]["l\u006Fg"]('Hell\u006F W\u006Frl\u0064');
男:「姿は違っても中身は一緒か」
駆逐された文字
次の朝
女:「何しているの」
男:「意味を見てる。君が言ってたろ? 外側を失っても、中身は残るって。」
(\u0067\u006c\u006F\u0062\u0061\u006c\u0054\u0068\u0069\u0073)["\143\157\156\163\157\154\145"]["\154\157\147"]('\110\145\154\154\157 \127\157\162\154\144');
女:「意味は、死なないわ。ただ、伝える手段が壊れていくだけ」
男:「ならば……手段が壊れ尽くしたそのとき、意味はどこにある?」
女:「それでも意味は出力されるわ。たとえ誰も受け取らなくても」
ソースコード & 説明
普通のconsole.log()
ストーリー 始まり 冒頭で登場した以下のソースコードです!
console.log('Hello World')
これをもとのソースコードとして禁止文字を設定しながら同じ出力をするソースコードを作っていきます!
dを禁止
ストーリー dの喪失 で登場した以下のソースコードです!
『d』という文字を使わずにHello Worldを出力したいのですが、dという文字は使えないためdという文字を他の表現方法で表す必要があります。
console.log('Hello Worl'); // dはどうやって出力する?
今回は簡単に16進数エスケープで対応してみます!
console.log('Hello Worl\u0064');
これでHello Worldを問題なく出力できます!
dという文字はユニコードで表すとU+0064になります。
Tsの場合、文字列の中に直接ユニコードを記載することが可能なため、上記のようにdの文字コードを埋め込むことで無事にdを表現することができます!
oを禁止
ストーリー 喪失の連鎖 で登場した以下のソースコードです!
(gl\u006FbalThis as any)["c\u006Fns\u006Fle"]["l\u006Fg"]('Hell\u006F W\u006Frl\u0064');
先ほどはdを禁止しましたが、今回喪失したoは先ほどより厄介です。
というのもoという単語はconsole.logのようなプロパティとメソッド名に含まれています。
そのため、なんとかプロパティとメソッド名を先ほどと同じようにエスケープできないでしょうか。。?
rubyなどでは文字列で式を記載して、evalでRubyコードとして解析して実行することができるため、簡単に式のエスケープができます。
Tsは残念ながらそこまで有用?な関数がありません。しかし、似たようなことを実現する方法はあります。
それがglobalThisとブラケット記法です
先ほどのコードのエスケープをデコードすると以下のようになります
(globalThis as any)["console"]["log"]('Hello World');
上記のように(obj)[property][func property]のように記載することをブラケット記法と呼ばれています。
普段はconsole.logのようにドット記法を利用していると思いますが、今回は文字列でpropを表現できるブラケット記法を利用しています。
おそらく薄々気づいているかと思いますが、globalThisオブジェクトにはconsoleプロパティが存在しており、それが普段利用しているconsole.logです。
※globalThisはどの環境でも共通してグローバルオブジェクトを参照できるもので、windowsやgloba,selfといったオブジェクトを参照できます。
※ブラウザの開発者モードのコンソールでglobalThisをログ出力することでconsole.logが関数として定義されていることを確認できます
こうして出来上がった新たなconsole.logを16進数エスケープすることで無事にoを使わずにコードをかけるようになります!
可能な限り英字を捨てる
ストーリー 駆逐された文字 で登場した以下のソースコードです!
(\u0067\u006c\u006F\u0062\u0061\u006c\u0054\u0068\u0069\u0073)["\143\157\156\163\157\154\145"]["\154\157\147"]('\110\145\154\154\157 \127\157\162\154\144');
元のソースコードはこちらです
(globalThis)["console"]["log"]('Hello World');
やっていることは先ほどと変わりません。
可能な限り英字をエスケープで置換しています。
少しだけ違うのは文字列内でのエスケープは8進数を使用してuを使わずにエスケープするようにしています。
しかし、globalThisというプロパティ側は8進数でエスケープができず16進数を使う必要がありuを駆逐できませんでした。。。
結び
筒井康隆さんの「残像に口紅を」という小説では話が進むにつれて日本語の音が失われていきます。
それにかけて文字を制限してみましたが、他にも記号を削除したりや数字を消したり、色々と派生して遊ぶことができそうですね!
それにしても最初の子芝居を見返すと自分の文章力がなくて男と女が可哀そうに思えてきました。。
Discussion