早速ですが、こちらに書いてあるソースコード、実際に動くコードとして作成しました。実行結果はどのようになるでしょう?
答えはこれから各種イベントで配られるノベルティを受け取って打ち込んでみてください!!!
まずはあすから行われるコンピュータビジョンの学会MIRU2024のスポンサーブースで配布します!!!
と、いうのは冗談で、さすがに受け取れたとして打ち込みが大変ですし、以下にソースコードを貼り付けます。 本稿では以下のQuineの作り方について解説していきます。ただし、難読プログラミングが好きな人はまずは解説を読まずに自力で読んでみてください!
exec('''m=lambda_x:exec("".join(x.split()).replace("~",chr(32)),globals())'''.replace("_",chr(32))); m("""import~base64~as~b6;import~zlib;tr=lambda~x,y,z:~(y~if~x~else~z);dba=lambda~x:zlib.decompress(b 6.b64decode(x),-15).decode();a=1;lme=lambda~x:list(map(lambda~x:tr(x=="1",1,0),x));s="ZXhlYygnJydtPW xhbWJkYV94OmV4ZWMoIiIuam9pbih4LnNwbGl0KCkpLnJlcGxhY2UoIn4iLGNocigzMikpLGdsb2JhbHMoKSknJycucmVwbGFjZS giXyIsY2hyKDMyKSkpO20oIiIiaW1wb3J0fmJhc2U2NH5hc35iNjtpbXBvcnR+emxpYjt0cj1sYW1iZG F+eCx5LHo6fih5fm lmfnh+ZWxz ZX56KTtkYmE9bGFtYmRhfn g6emxpYi5kZWNvbXB yZXNzKGI2LmI2 NGRlY29kZSh4 K SwtMTUpLmRlY29kZSgpO2 E9MTtsbWU9bGFtYmR hfng6bGlzdC htYXAobGFtYmRhf ng6dHIoeD09IjEiLDEs MCkseCkpO3M9Int9Ijt kaT16bGliL mRlY29tcHJlc3Mo YjYuYjY0ZGVjb2Rl KCI3WlpCRG9Vd0NFVDN QUTI5LytX K2lab0NuUU crSmhxTnJDeU1QQ WVpc2ZVTG9uMlFX eUd5UkFBUkhid0RsOUNpY1NJOWhwUV JUdURIRlZG S05VU2hrTGxWT0V 1ZFBRSEpOM1lF SXJYQ1NZaVVDam5EUTlZeWFMYWVDaEN rUUU1QXMrM FFRNmhaNEVUcmpj RVlJajVTaUoy WXZlQVVUOHNndXQrNG9iQVRKS09Rb 1p6bVZxWmdD T29IY3pVbnU0eE Q wTXYvSnlSM 0F sdFNpRWw1MFF3SnhCUUNuMlFrMn Fqb1ZSdjVmckR mTDJRUVRGaERpSV Rz YU83REsvM nBQ eEl2aC93QSIpLC0xNSkuZG Vjb2RlKCk7cH JpbnQoIiIuam9p bihb dHIoYz0 9Ij EiLGI2LmI2NGRlY29kZS hzLmVuY29k ZSgidXRmLTgiKS kuZGV jb2R lKCJ 1dGYtOCIpLmZvcm1hdCh zKVtzd W0obG1lK GRpKVs6al0pXSx 0cihjP T0 iMCIs Y2hyKDMyKSxjKSlmb3IoaixjKWluKGV udW1lcmF 0ZShkaSkpXSkpI iIiKSMj I yMjIyMj IyMjIyMjIyMjIyMjIyMjIyMjIyMjIyM jIyMjIyM jIyMjIyMjIyMj IyMjIyN 3ZV9hcm VfaGlyaW5nPXJlcXVlc3RzLmdldCgi aHR0cHM 6Ly9qb2JzLm0z LmNvbS9l bmdpbmVl ci8iKSMjIyMjIyMjIyMjIyMjIyMjIy MjIyMjIy MjIyMjIyMjIyM jIyMjIyMj IyMjIyMjIy Mj";di=zlib.decom press(b6.b6 4decode(" 7ZZBDoUwCET3P Q29/+W+iZoC nQG+JhqNrCy MPAeisfULon2QWy GyRAARHbwD l9CicSI9hp QRTuDHFV F K NUShkLl VOEudPQ H J N3YEIrXCSY iUCjnDQ9YyaL aeChCkQ E5As+0QQ 6hZ4ETrj cEYIj5SiJ2Yv eAUT8sgut+4obAT JKOQoZzmVqZgCOoHczUnu4xD0Mv/JyR3AltSiEl50QwJxBQCn2Qk2qjoVRv5frDfL2QQTFhDiITsaO7DK/2pPxIvh/wA"),-15). decode();print("".join([tr(c=="1",b6.b64decode(s.encode("utf-8")).decode("utf-8").format(s)[sum(lme( di)[:j])],tr(c=="0",chr(32),c))for(j,c)in(enumerate(di))]))""")##################################### ###############we_are_hiring=requests.get("https://jobs.m3.com/engineer/")##########################
ということで、改めまして、AI・機械学習チームリーダーの大垣@Hi_kingです。 本稿はエムスリーAI・機械学習チームで2週間連続で行われるブログリレー1日目の記事となります!
- エムスリー難読プログラミング部
- 難読プログラミングジャンル、整形Quineをやろう!
- 自分で整形したPython Quineを作ってみたい方へ
- まずはとりあえず整形ではないQuineを作る(赤パート)
- 次に、AAを圧縮する(青パート)
- 展開されたAAの形に、展開したプログラムをprintする (橙パート)
- Pythonで改行や空白を自由に整形する (紫パート)
- まとめ
- We are hiring !!
エムスリー難読プログラミング部
エムスリーエンジニア公式X をフォローいただいている方は、朝のポストで流れてくるからご存知かと思いますが、エムスリーでは難読プログラミング部が盛んに活動しています。
VPoE河合のモーニングルーティン☀️
— エムスリー エンジニア公式 (@m3_engineering) 2024年4月12日
金曜は難読クイズです!#m3_morning
今日はちょっと難しめ!わかるかな?
list(map(list,list(map(map,map(lambda map:list,map:='map'),map)))) pic.twitter.com/T2Hp2QEsmK
せっかくなのでこの盛り上がりを会社の外でも知ってもらおうということで、#crazy_programmingチャンネルで以下のように呼びかけられました
その中から、今回は、まず機械学習エンジニア向けということで、Python難読おしゃれノベルティを作ってみた次第です。
難読プログラミングジャンル、整形Quineをやろう!
さて、難読プログラミング界隈が好きな人は、最初に見た目で予測がついたかと思いますが、こちら、Quine となっています。
クワイン(英: Quine)は、コンピュータープログラムの一種で、自身のソースコードと完全に同じ文字列を出力するプログラムである。
つまり、それ自体が実行可能で、かつ出力した文字列が自分自身のソースコードとなります。別の言い方をすると、exec(exec(exec(quine)))...と何度でも実行可能なコードとも言えますね。そして、Quineのなかでも、Quineの機能を保ったまま色んな機能をつけるという遊び方があり、今回作ったQuineは、見た目に意味を持たせたQuineです。
さて、このような整形Quineを作るうえで難しいポイントは以下の3つになります。解説の見通しを良くするためにパートごとに色を付けたコードもおいておきます。
- ソースコード自分自身を出力するために、自分の情報を圧縮して自分に埋め込むこと (赤パート)
- 狙った形に整形すること (青パート、橙パート)
- 改行や空白が重要なPythonで上記を実現すること (紫パート、緑パート)
自分で整形したPython Quineを作ってみたい方へ
Jupyter notebookを用意しました。ちょっと形状によってコメントアウトの文字数とか弄る必要がありますが、基本的にすぐに動くQuine作れると思います!Quine文化を流行らせていきたいので、皆さんぜひ作ってみてください。完成作品のコメントお待ちしてます。
まずはとりあえず整形ではないQuineを作る(赤パート)
さて、ここからは動作原理を知りたい難読プログラミングオタク向け文章です。
Quine全体のうち最も大きな部分を占める赤パートですが、こちらはPythonでQuineを書いている先達に習って、プログラムをbase64でプログラム自身に埋め込む戦略を使わせてもらいました。
【Python3】Base64を使ってQuine - Jaded Electric Sphere
import base64 as b s="aW1wb3J0IGJhc2U2NCBhcyBiCnM9Int9IgpwcmludChiLmI2NGRlY29kZShzLmVuY29kZSgidXRmLTgiKSkuZGVjb2RlKCJ1dGYtOCIpLmZvcm1hdChzKSk=" print(b.b64decode(s.encode("utf-8")).decode("utf-8").format(s))
この時点で見た目がいかついっちゃいかついんですが、要点としては、プログラム全体をbase64エンコードしておくことで、任意のプログラムをQuineにできることです。つまり、このコードの出力部分に整形機能を足していく(+全体からスペースとか改行を消していく)ことで、整形Quineが完成すると見通せます。
次に、AAを圧縮する(青パート)
d i=zlib.decompress (b6.b64decode( "7dRJDoA gDAXQPac p97+cGxPp 8DuIGBZ0Y2i1j1S09R +iHWR3hJ7osEK5 QgbBrQik oTOHDBlP cRBCeRJZsAy Q+6rzvA0aXgmRY3H2ym9 LIv0NUh0X35TI2 ohTQwhTQk QdiToik98 hpue2wlACUbngd KWP8PhEHkF1jPAP0N/qLEJr EeO/760WIapn/cWPStCVhzImEYPRRIDk5hEIGlkTB9kQuQA="),-15).decode()
の部分ですね
展開すると、こちらでやってることは要するに
import base64 as b6 import zlib di=zlib.decompress( b6.b64decode( "7dRJDoAgDAXQPacp97+cGxPp8DuIGBZ0Y2i1j1S09R+iHWR3hJ7osEK5QgbBrQikoTOHDBlPcRBCeRJZsAyQ+6rzvA0aXgmRY3H2ym9LIv0NUh0X35TI2ohTQwhTQkQdiToik98hpue2wlACUbngdKWP8PhEHkF1jPAP0N/qLEJrEeO/760WIapn/cWPStCVhzImEYPRRIDk5hEIGlkTB9kQuQA=" ),-15 ).decode()
出力は、
1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 1111111111111100000000000111111111111111000000000000111111111111100000000000011111111111111111111111 1111111111111100000000000011111111111110000000000000111111111100000000000000000011111111111111111111 1111111111111100000000000011111111111110000000000000111111111000000000110000000001111111111111111111 1111111111111100000000000001111111111100000000000000111111110000000111111110000000111111111111111111 1111111111111100000011000001111111111100000010000000111111100000001111111111000000011111111111111111 1111111111111100000011000000111111111000000110000000111111111111111111111111000000111111111111111111 1111111111111100000011100000111111111000000110000000111111111111111111111110000000111111111111111111 1111111111111100000011100000011111110000001110000000111111111111111111111000000011111111111111111111 1111111111111100000011110000001111110000001110000000111111111111111110000000001111111111111111111111 1111111111111100000011110000001111100000011110000000111111111111111110000000001111111111111111111111 1111111111111100000011111000000111100000011110000000111111111111111111111100000000111111111111111111 1111111111111100000011111000000111000000111110000000111111111111111111111111000000011111111111111111 1111111111111100000011111000000011000000111110000000111111111111111111111111100000001111111111111111 1111111111111100000011111100000010000001111110000000111111111111111111111111100000001111111111111111 1111111111111100000011111100000000000001111110000000111111111111111111111111100000001111111111111111 1111111111111100000011111110000000000011111110000000111111100000001111111111100000001111111111111111 1111111111111100000011111110000000000011111110000000111111110000000111111111000000011111111111111111 1111111111111100000011111111000000000111111110000000111111111000000000000000000000111111111111111111 1111111111111100000011111111000000000111111110000000111111111110000000000000000011111111111111111111 1111111111111100000011111111100000001111111110000000111111111111110000000000011111111111111111111111 1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111
ってことです。
こちらの実装は、jsで整形Quineを実装されていた id:shoponpon の記事を参考にさせていただきました。
AAの作成は AA変換(アスキーアート生成)|Web便利ツール@ツールタロウ を利用させていただきました。
展開されたAAの形に、展開したプログラムをprintする (橙パート)
青パートでAAを01の列にできて、赤パートでプログラムをエンコード・デコード出来たので、あとは組み合わせて文字列にするだけです
print("".join([tr(c=="1",b6.b64decode(s.encode("utf-8")).decode("utf-8").format(s)[sum(lme( di)[:j])],tr(c=="0",chr(32),c))for(j,c)in(enumerate(di))]))
展開するとこういう感じになります。
tr=lambda x,y,z: (y if x else z) lme=lambda x:list(map(lambda x:tr(x=="1",1,0),x)) print( "".join([ tr( c=="1", b6.b64decode(s.encode("utf-8")).decode("utf-8").format(s)[sum(lme(di)[:j])], # プログラムをデコード tr(c=="0",chr(32),c) ) for(j,c) in (enumerate(di)) # アスキーアート01の文字列についてのループ ]) )
さて、ここで普通のプログラムを書きなれてる人には、なんでループの中で毎回プログラムをデコードしてるんだろう?ってなるかと思います。
p = デコードされたプログラム for c in アスキーアート: if c==1: pから1文字ずつ出力
と書くのがシンプルですので。ただし、これをやるためには、pの何文字目まで来たかのカウンターをあらたに導入する必要があります。一方で先程のように、出力時に毎回 sum(lme(di))[:j]
という形で、jまでに何回1があったはずかを毎回計算すると、計算的には無駄が多いですが、プログラム上は不要なクラスを作らずすんで、コードゴルフ的な考えでお得です。なるべくコードが短いほうがデザインは自由が効くので*1。この辺は標準ライブラリでもっと短縮できる方法とかもあるかもしれません。要研究です。
Pythonで改行や空白を自由に整形する (紫パート)
空白と改行をなくすための基本的なアイディアはシンプルで、2行目から後ろを見ていただくと、
m("""import .... """)###
このような形で、複数行文字列 + コメントという形式になっているのがわかります。つまり、この m("""
以降は文字列として扱われるので、改行や空白を自由に入れられるわけです。
それを1行目の
m=lambda x:exec("".join(x.split()).replace("~"," ")
という関数で包むことで、評価のときには、空白は無視されて、~は空白として扱われる、わけです。
シンプルにはmという関数をあらたに導入しないでも、exec("".join("""任意のプログラム""").split())
という形で書けそうなのですが、
- 関数宣言で空白がどうしても避けられない(def にせよ lambda にせよ空白が必要)という点から、
replace("~", " ")
として、~を空白にreplaceしてから評価する - メソッドを後置する形だと、メソッドの中に改行が来たときに実行不可能になるので、必須機能は空白のない1行目に集約するほうがデザインの自由度が高まる
という点でmという関数を事前定義してexecの代わりとしました。この辺はPythonならではの難しさであり、replaceでどうにかしたのとか、1行目を改行出来ないデザインにしたのとかは自分の中でもちょっと逃げだと思ってるので、良い手段があればぜひコメント下さい!
ちなみにm自身の定義は
exec('''m=lambda_x:exec("".join(x.split()).replace("~",chr(32)),globals())'''.replace("_",chr(32)));
という形で、_を空白にreplaceしてevalで実現してます(空白をchr(32)とすることで空白文字がなくなってることにも留意)。
ところで、これ書いてるときにハマったのが、
m=lambda y: exec(y) m("a=1;b=lambda:print(a);b()")
の形で書くとexecのスコープがlambdaの中で厳しいスコープとなり、execの中で他の変数(この場合a)を参照できなくてエラーになることでした。解決策としては m=lambda y: exec(y, globals())
の形で、lambdaの外側のスコープを注入する必要があります。
まとめ
本稿では上記の整形Quineの作成過程を追ってきました。整形ができるようになったのはQuineへの入口の第一歩にしか過ぎないので、ここから、実行のたびに切り替わっていくQuineだったり、入力を受け付けてゲームとして遊べるQuineだったり、わけのわからない世界に足を踏み込んでください。私も次は動きのあるQuineに挑戦したいです。
We are hiring !!
エムスリーでは、難読プログラミングは出来つつも難読ではないプロダクションコードを書くエンジニアを募集しています!ぜひ入社して社内でQuine文化を流行らせましょう!
私の本業は難読プログラミングエンジニアではなく機械学習エンジニアなので、一緒にMLやるエンジニアも募集してます!
エンジニア採用ページはこちら
カジュアル面談もお気軽にどうぞ
インターンも常時募集しています
*1:じつはここを書いている時点では、関数宣言とクラス宣言をゼロにしてこのあとの姑息な手を使わずとも空白なしで行けるのを目指してたから、という理由もあります