はじめに
こんにちは。エンジニアリンググループ マルチデバイスチームGMの藤原聖です。
エムスリーは2025年9月10日(水)〜12日(金)に開催される『DroidKaigi 2025』のゴールドスポンサー、2025年11月1日(土)に開催される『Kotlin Fest 2025』のゴールドスポンサーをそれぞれ務めます。
そこでKotlinを愛する皆さまに楽しんでいただこうとクリアファイルを作成しました。上記のイベントの参加者向けノベルティやブース等で配布しますので、ぜひお手に取ってご覧ください。

本記事はクリアファイルに記載されているコードの解説記事となります。
Kotlin コード
早速ですがソースコード全体は次のようになります。お手元の実行環境やKotlin Playground等で実行してみてください。
typealias S=String;/*Kotlin is */fun main(){var a=mutableListOf<S>();val b="";val(x,y,z)=Triple({s:S
->s.map{if(it=='0')a.removeLast()else" "}},{b:Byte->(b.toInt()and 255).toString(2)},{s:S->s.replace(
Regex("\\s"),b)});val (c,d)=Pair(Regex("\\R"),{s:S->java.util.Base64.getDecoder().decode(z(s))});"""
dHlwZWFsaWFzIFM9U3RyaW5nOy8qS290bGluIGlzICovZnVuIG1haW4oKXt2YXIgYT1tdXRhYmxlTGlz dE9mPFM+KCk7dmFs
IGI9IiI7dm FsKHgseSx6KT1UcmlwbGUo e3M6Uw0KLT5zLm1hc HtpZihpdD09Jz
AnKWEucmVtb3 Z lTGFzdCgpZWxzZSIgIn19 LHtiOkJ5dGUtPihiL nRvSW50KClh
bmQgMjU1KS50b1N 0cmluZygyKX0se3M6Uy 0+cy5yZXBsYWNlKA0KU mVnZXgoIlx
ccyIpLGIpfSk7dm FsIChjLGQpPVBhaX IoUmVnZXgoIlxcUiIpL HtzOlMtPm phdmEudXRp
bC5CYXNlNjQuZ2V 0RGVjb2RlcigpLm RlY29kZSh6KHMpKX0pOyIiIg0KJXMN CiIiIi5ydW
57c3BsaXQoIjoiK S5sZXR7YT1TLm Zvcm1hdChkKGl0WzBdKS5kZWNvZGVUb 1N0cmluZyg
pLnJlcGxhY2UoYy xiKSx6KHRoaX MpKS5yZXZlcnNlZCgpLm1hcHtpdCs NCmJ9LnRvTX
V0YWJsZUxpc3Qo K Tt4KGQoaXR bM V0pLmpvaW5Ub1N0cmluZyhiKXt5 KGl0KS5wYWRTd
GFydCg4LCcwJyl9 Ky IwIi5yZXB lYX QoMHg1OCkpLmNodW5rZWQo MTAwKS4NCmZv
ckVhY2h7cHJpbn Rsbi hpdC5qb 2lu VG9TdHJpbmcoYikpfX19 fS8vPT09PT
09PT09PT09PT09 PT09P T09P T09P T09PT09PT09PT09IFdlI GFyZSB oaXJpbmc
hID09PT09Ly8NC i8vPT0 9P T0gRm luZCB0aGlzIFF1aW5lIGF0Omh0dHBzO i8vd3d3L
m0zdGVjaC5ibG9 nL2VudH J 5L2tvdG xpbi1xdWluZSA9PT09PT09IEpvaW4gT TMgPT09P
T09PT09PT09Ly 8=:AAAA AAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAA
AAAAAAAAAAAAA AAPAAAAP /4AAAf/w AAf/gAAAL/gAAD/8AAH//gAAAH/AAA f8AAB///
AAAAf/AAD/wAA HAH8AAAB/ 8AAf/gAAAA fwAAAH/4AD/8AAAAB /AAAAf/wAP/ 4AAAAP4AA
AD7/gB5/gAAAD +AAAAHn+APH +AAAH/8AAAA 8P8B8f4AAB//8AA ADwf8Ph/wA ADA/8AAAPA
/58H+AAA A A /wAAA8B /vgP8AA A A B/AAAHwH/8 B/wAAAAP+AAA
fAP/gH/ AAAAA/wA AB8Af8AP 8AAGAD+AAAHg A/gA/wAB4AfwAAN
/sB+Av/2AH//8AAB//wDwD//4AH/+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
""".run{split(":").let{a=S.format(d(it[0]).decodeToString().replace(c,b),z(this)).reversed().map{it+
b}.toMutableList();x(d(it[1]).joinToString(b){y(it).padStart(8,'0')}+"0".repeat(0x58)).chunked(100).
forEach{println(it.joinToString(b))}}}}//==================================== We are hiring! =====//
//===== Find this Quine at:https://www.m3tech.blog/entry/kotlin-quine ======= Join M3 ============//
Quineクリアファイル
実行された方はお気づきかと思いますがこのソースコードはQuineになっていて、大垣さんの作ったPython版を元に、藤原幹大さんがSwift版を作り、それを元に私がKotlin版を作成しました。社内でQuineクリアファイルブーム?!が来ているので、今後いろんな言語のQuineクリアファイルが作成されるかも?!
Kotlin Quine コードの動作原理
基本的には下記のPython版の作り方と同様なので、興味のある方は最初に次の記事をご覧いただければと思います。本記事ではPython版からの変更点やKotlin独自の実装について簡単に解説します。
前提
Quineとして成り立たせるため、宣言しなくても良い変数を定義したり、使わなくても良いTriple/Pairなどを使って変数を定義したり、使わなくて良い場所でスコープ関数を使ったり、10進数表記で良いところを16進数で表記したりしています。その結果、可読性も低く、コードゴルフのように最短にもなっていないことをご理解ください。特にPython版はexec関数を用いて実行しているため、コードの途中に改行や空白を入れることができましたが、Kotlin版ではそれができず、1行100文字という制約の中、改行可能な位置を探りながらコーディングしているので、上記のような文字数合わせのための処理が入っています。
前半部分のコード
Base64文字列より前の部分をフォーマットすると次のようなコードになります。文末の;は不要ですが、残しています。
typealias S = String; /*Kotlin is */fun main() { var a = mutableListOf<S>(); val b = ""; val (x, y, z) = Triple( { s: S -> s.map { if (it == '0') a.removeLast() else " " } }, { b: Byte -> (b.toInt() and 255).toString(2) }, { s: S -> s.replace(Regex("\\s"), b) }); val (c, d) = Pair( Regex("\\R"), { s: S -> java.util.Base64.getDecoder().decode(z(s)) } );
String型は頻出するので、typealiasを用いてSと短い名前をつけています。
/*Kotlin is */funは説明不要ですね!
変数がいくつか宣言されていますが、bとcは文字数調整の都合上、宣言している変数です。
aおよびxが全体の処理の中心となる変数です。後半のコードで、出力する全文字が逆順にリストaへ格納され、xにはリストaから1文字ずつ文字を取り出す処理が格納されています。変数dにはBase64デコード処理のための関数が入っています。
後半部分のコード
Base64文字列より後の部分をフォーマットし、前半で宣言した(意味のない)変数bとcを展開すると次のようなコードになります。
""" //Base64文字列 """.run { split(":") .let { a = S.format( d(it[0]) .decodeToString() .replace(Regex("\\R"), ""), z(this) ) .reversed() .map { it + "" } .toMutableList(); x( d(it[1]) .joinToString("") { y(it).padStart(8, '0') } + "0".repeat(0x58) ) .chunked(100) .forEach { println(it.joinToString("")) } } } }
まずはBase64文字列をsplit(":")を用いて2つに分割し、それをletに渡しています。以後、letの引数のラムダ式内の変数it[0]がPython版の解説にある「コード全体のBase64表現」となり、変数it[1]が「AAの圧縮表現」となります。
「コード全体のBase64表現」には実はBase64文字列そのものは含まれておらず、Base64文字列の部分にはフォーマット指定子%sが記載されています。そのため、最初の処理ではString.formatを用いて、その指定子をBase64文字列に置換しています。String.formatの第一引数には「コード全体のBase64表現」から空白・改行を除去し、Base64デコードした文字列を与え、第二引数にはBase64文字列そのもの(this)から空白・改行を除去した文字列を与えています。その結果を逆順にし(reversed())、文字列のMutableListにしたものをaに代入しています。
「AAの圧縮表現」が格納された変数it[1]もBase64デコードし、全体の文字数と合うように0埋め処理をしたのち、前半で定義した変数xに渡しています。xの処理では、「AAの圧縮表現」から得られたバイナリ文字列の各文字について、0であればリストaの末尾から1文字を取り出し、1であれば空白スペースに置き換えています。この空白スペースが「M3」の文字部分となります。
最後にchunked(100)で100文字ずつ分割し、printlnで1行ごとに出力すればAAの完成です。
最後に
説明を省略した部分もありますが、KotlinのQuineコードの面白さを少しでも感じていただければ幸いです。さらなる工夫のアイデアや、感想、別パターン作ったよ!などなどあれば以下のイベント等のブースで藤原聖までお声がけいただけると幸いです。
- DroidKaigi 2025 (2025年9月10日(水)〜12日(金)開催)
- Kotlin Fest 2025 (2025年11月1日(土)開催)
We are hiring!!
エムスリーでは、エンジニア、特にKotlinを愛でるスマホアプリエンジニアを大大大大絶賛募集しています! 少しでも興味を持ちましたら、ぜひカジュアル面談などでお話しましょう!