エムスリーテックブログ

エムスリー(m3)のエンジニア・開発メンバーによる技術ブログです

エムスリーがKotlin Loverに贈るノベルティ、Kotlin Quineクリアファイルを作りました

はじめに

こんにちは。エンジニアリンググループ マルチデバイスチームGMの藤原聖です。

エムスリーは2025年9月10日(水)〜12日(金)に開催される『DroidKaigi 2025』のゴールドスポンサー、2025年11月1日(土)に開催される『Kotlin Fest 2025』のゴールドスポンサーをそれぞれ務めます。

そこでKotlinを愛する皆さまに楽しんでいただこうとクリアファイルを作成しました。上記のイベントの参加者向けノベルティやブース等で配布しますので、ぜひお手に取ってご覧ください。

Kotlin Quine クリアファイル

本記事はクリアファイルに記載されているコードの解説記事となります。

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独自の実装について簡単に解説します。

www.m3tech.blog

前提

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は説明不要ですね!

変数がいくつか宣言されていますが、bcは文字数調整の都合上、宣言している変数です。 aおよびxが全体の処理の中心となる変数です。後半のコードで、出力する全文字が逆順にリストaへ格納され、xにはリストaから1文字ずつ文字を取り出す処理が格納されています。変数dにはBase64デコード処理のための関数が入っています。

後半部分のコード

Base64文字列より後の部分をフォーマットし、前半で宣言した(意味のない)変数bcを展開すると次のようなコードになります。

"""
//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コードの面白さを少しでも感じていただければ幸いです。さらなる工夫のアイデアや、感想、別パターン作ったよ!などなどあれば以下のイベント等のブースで藤原聖までお声がけいただけると幸いです。

We are hiring!!

エムスリーでは、エンジニア、特にKotlinを愛でるスマホアプリエンジニアを大大大大絶賛募集しています! 少しでも興味を持ちましたら、ぜひカジュアル面談などでお話しましょう!

jobs.m3.com