以下の内容はhttps://www.m3tech.blog/entry/swift-quineより取得しました。


引数でアスキーアートが変化するSwift Quineの仕組みをフローチャートで解説

この記事はエムスリー Advent Calendar 2025 3日目の記事です。

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

皆さんはQuine(クワイン)をご存知でしょうか。 Quineとは、「自身のソースコードと完全に同じ文字列を出力するプログラム」のことです。

今回紹介するのは、引数でアスキーアートの形が変わるSwift製Quineです。一見難解なコードの裏にあるロジックを、フローチャートを使って紐解いていきます。

Swift Quine:M3のすがた

まずはコードを見てください。これが今回の主役です。

import Foundation;typealias S=String;var(e,n)=("\u{20}",CommandLine.arguments.count%2^1),s={(i,j)in(
0..<j).map{if(i%2==0){a.remove(at:$0&0)}else{e}}},p={b($0+20*n).enumerated},b={Data(base64Encoded:S(
l.split(separator:":")[$0]))!},r={$0+"\n"},o={S(format:S(data:b(0),encoding:.utf8)!,~$0&1,$1)},l="""
:aW1wb3J0IEZvdW5kYXRpb247dHlwZWFsaWFzIFM9U3RyaW5nO3ZhcihlLG4pPSgiXHV7MjB9IixDb21    tYW5kTGluZS5hcmd
1bWVudHMuY             291bnQlJTJeJWQpLHM9eyh            pLGopaW4oCjAuLjxqK            S5tYXB7aWYoaS
UlMj09MCl7YS           5yZW1vdmUoYXQ6JDAmMCl          9ZWxzZXtlfX19LHA9e2                IoJDArMjAqb
ikuZW51bWVyYXRl         ZH0sYj17RGF0YShiYXN         lNjRFbmNvZGVkOlMoCm     wuc3Bs        aXQoc2VwYX
JhdG9yOiI6IilbJ           DBdKSkhfSxyPXskM          CsiXG4ifSxvPXtTKGZv   cm1hdDpTK       GRhdGE6Yig
wKSxlbmNvZGluZz           oudXRmOCkhLH4kM           CYxLCQxKX0sbD0iIiIKJUAiIiIuZmls       dGVyeyEoJD
A9PSJcbiJ8fFMoJ            DApPT1lKX0sYT            1sLm1hcHtTKCQwKX07cHV0cyhvKG4sK      DEuLjwyMSku
bWFwe3IocCgkMCk             oKS5tYXB7cyg            kMC4wLEludCgkMC4xCikpLypHZXQqL      y5qb2luZWQvK
mluLCovKCl9LypT    d         GF5Ki8uam9    pb       mVkLyp0byB0aGUgbWlzc2lvbiw        qLygpKX0vKkNyZ
WF0ZSovLmpvaW5l    ZC        8qaW5ub3Z    hdG        lvbiEqLygpKSkKLy89PT09P            T09PT09PT09P
T09PT09PT0gV2U    gYXJ        lIGhpcm     luZ        yAhISA6IGh0dHBzOi8va                 m9icy5tMy5
jb20vZW5naW5lZ    XIvID         09PT     09PT        09PT09PT09PT09PT09PT09PT0vLw=         =:UAQQ:Cg
0WDBIMDQ==:DAs    VChMQC         w=     =:Dwk        TCRMFBggK:DwsQChMDCQcK:DwsPCx8H        Cg==:Dww
NDB8GCw==:Dw0M    DB4GDA=        =     :DwQBC        QoEAgcaCA4=:DwQCCAkEAwgXDAw=:DgQ       ECAcFAwg
UEQo=:DgQFCQQ    FBAgdCQk             =:DgQGCQ        IFBQgfCAg=:DgQHCAEFBgggBwg=:DQQ       IDQgIHwc
I:DQQJCwkIHgg    I:DQQKCQo           IEAMLBwk=        :DQQLBwsIDwQKBwo=:CA4HBgcRChMM        :CA4IBAg
RDA4P:ZA==:ZA    ==:EAYPBgE         RARYQ:EAcN        MBA=:EAgLMRA=:EA   kJEAUdEA==:       EAoHCgwHC
AYY:EAwDDAwHC    AYY:EBsFDQk       GGA==:EBsFD        AoGGA==:EBsFDQk    GGA==:EAYD       CQMGDAcIBh
g=:EAYFB              QUGDAcI      Bhg=:EA                 YPBgwHCAYY                   :EAYPDQUHBgo
W:EAYPGQ              YKFg==:E    AYPGQYKF                 g==:EAYPBgER              BwoW:ZA==:ZA==:
""".filter{!($0=="\n"||S($0)==e)},a=l.map{S($0)};puts(o(n,(1..<21).map{r(p($0)().map{s($0.0,Int($0.1
))/*Get*/.joined/*in,*/()}/*Stay*/.joined/*to the mission,*/())}/*Create*/.joined/*innovation!*/()))
//===================== We are hiring !! : https://jobs.m3.com/engineer/ =========================//

Swiftのコード全体で「M3」という文字のアスキーアートを形成しています。

以前にも iOSDC Japan 2025専用のSwift Quineを作成したことがあるのですが、今回は「汎用Swift Quine」として、改めて設計し直しました。

変身する実行結果:M3Tのすがたへ

このQuineの特徴は、実行方法によってその出力結果が変わる点にあります。

1. 通常モード(引数なし) ターミナルで swift m3.swift のように実行すると、上記のソースコードと全く同じ、「M3」アスキーアートが出力されます。

2. 変身モード(引数あり) しかし、swift m3.swift change のように引数を渡すと、出力結果が変化します。

import Foundation;typealias S=String;var(e,n)=("\u{20}",CommandLine.arguments.count%2^0),s={(i,j)in(
0..<j).map{if(i%2==0){a.remove(at:$0&0)}else{e}}},p={b($0+20*n).enumerated},b={Data(base64Encoded:S(
l.split(separator:":")[$0]))!},r={$0+"\n"},o={S(format:S(data:b(0),encoding:.utf8)!,~$0&1,$1)},l="""
:aW1wb3J0IEZvdW5kYXRpb247dHlwZWFsaWFzIFM9U3RyaW5nO3ZhcihlLG4pPSgiXHV7MjB9IixDb21tYW5kTGluZS5hcmd1bWV
udHMuY291bnQlJTJeJWQpLHM9eyhpLGopaW4oCjAuLjxqKS5tYXB7aWYoaSUlMj09MCl7YS5yZW1vdmUoYXQ6JDAmMCl9ZWxzZXt
lfX19LHA9e2IoJDA      rMjAqbikuZW51bW      V                 y                      YXRlZH0sYj17RGF0
YShiYXNlNjRFbmNv       ZGVkOlMoCmwuc                                                3BsaXQoc2VwYXJhd
G9yOiI6IilbJDBdK        SkhfSxyPXsk                                                 MCsiXG4ifSxvPXtT
KGZvcm1hdDpTKGRh         dGE6YigwK                Sxlbm                             NvZGluZzoudXRmOC
khLH4kMCYxLCQxKX          0sbD0iI          iIKJUAiIiIuZ       mlsdGVye      yEoJDA9PSJcbiJ8fFMoJDApP
T1lKX0sYT1sLm1hc            HtT            KCQwKX07cHV0       cyhvKG4s      KDEuLjwyMSkubWFwe3IocCgk
MCkoKS5tYXB7cygk                           MC4wL             EludCgkMC      4xCikpLypHZXQqLy5qb2luZW
QvKmluLCovKCl9Ly                           pTdGF            5Ki8uam9pb      mVkLyp0byB0aGUgbWlzc2lvb
iwqLygpKX0vKkNyZ                           WF0ZS             ovLmpvaW5      lZC8qaW5ub3ZhdGlvbiEqLyg
pKSkKLy89PT09PT0      9PT         09P      T09PT09PT09P       T0gV2UgY      XJlIGhpcmluZyAhISA6IGh0d
HBzOi8vam9icy5tM      y5jb2     0vZW5      naW5lZXIvID0       9PT09PT0      9PT09PT09PT09PT09PT09PT0
vLw==:UAQQ:Cg0WD      BIMDQ==:DAsVChM      QCw==:DwkTCR       MFBggK:D      wsQChMDCQcK:DwsPCx8HCg==
:DwwNDB8GCw==:Dw      0MDB4GDA==:DwQB             CQoEA       gcaCA4          =:DwQCCAkEAwgXDAw=:DgQ
ECAcFAwgUEQo=:Dg      QFCQQFBAgdCQk=:                         DgQGCQ          IFBQgfCAg=:DgQHCAEFBgg
gBwg=:DQQIDQgIHw      cI:DQQJCwkIHggI                         :DQQKC          QoIEAMLBwk=:DQQLBwsIDw
QKBwo=:CA4HBgcRC      hMM:CA4IBAgRDA4      P                 :ZA==:Z          A==:EAYPBgERARYQ:EAcNM
BA=:EAgLMRA=:EAkJEAUdEA==:EAoHCgwHCAYY:EAwDDAwHCAYY:EBsFDQkGGA==:EBsFDAoGGA==:EBsFDQkGGA==:EAYDCQMGD
AcIBhg=:EAYFBQUGDAcIBhg=:EAYPBgwHCAYY:EAYPDQUHBgoW:EAYPGQYKFg==:EAYPGQYKFg==:EAYPBgERBwoW:ZA==:ZA==:
""".filter{!($0=="\n"||S($0)==e)},a=l.map{S($0)};puts(o(n,(1..<21).map{r(p($0)().map{s($0.0,Int($0.1
))/*Get*/.joined/*in,*/()}/*Stay*/.joined/*to the mission,*/())}/*Create*/.joined/*innovation!*/()))
//===================== We are hiring !! : https://jobs.m3.com/engineer/ =========================//

アスキーアート部分の形状が「M3T」になっています。この出力されたコードもまた、コンパイル可能で実行可能なQuineとなっています。さらに、「M3T」のQuineを引数ありで実行すると「M3」の形状に戻ります。

隠されたメッセージ:「M3T」とは?

「M3」は分かるけど、変身後の「M3T」って何? と思われた方もいらっしゃるかもしれません。 これは 「エムスリーテクノロジーズ (M3 Technologies)」 を意味しています。

www.m3t.co.jp

エンジニアリングへの遊び心を込めつつ、「エムスリーグループ全体をさらに加速させるべくチャレンジしているエムスリーテクノロジーズをよろしく!」という思いをこの形にしました。

日々どのような技術的挑戦をしているのか、その魅力について語っている記事がありますので、このQuineで興味を持ってくださった方はぜひ読んでみてください!

▶ エムスリーテクノロジーズの魅力を紹介する記事はこちら

データの解剖:「テンプレート」と「設計図」の秘密

さて、ここから技術的な解説です。 一体どうやって、コードの整合性を保ちながら形を変えているのでしょうか? その秘密は、コードの大半を占める変数 l(小文字のエル)にあります。

この l は以下のような特性を持っています。

  1. 空白と改行の除去: 変数 l は、コード内の Base64 文字列部分(見た目でアスキーアートになっている部分)から、改行コードと空白文字をすべて取り除いた状態で保持されています。
  2. 空白数の保存則: また、変身前後で整合性を保つため、アスキーアート部分に含まれる空白の総数は、M3モードとM3Tモードで全く同じになるように設計されています。
  3. M3とM3Tで「不変」: そのため、見た目のアスキーアートが「M3」であろうと「M3T」であろうと、プログラムとして読み込まれる変数 l の中身は完全に同じ文字列になります。

この l の中身をコロン : で区切ることで、以下のようにデータを使い分けています。

// lの中身の概念図
l = "テンプレートデータ:行データ1:行データ2:...:行データ40"

プログラムは l.split(separator:":") を実行し、分割されたデータをBase64デコードして以下のように使い分けています。

1. 骨組みを作る「テンプレート」

分割されたデータの最初の要素(インデックス0)は、アスキーアート部分以外の、プログラム全体の骨組み(テンプレート)です。 これをBase64デコードすると、import Foundation... から始まるコードが現れますが、「アスキーアートの文字列」と「アスキーアートがどちらのモードなのかの情報」が入る部分だけが %@ などのプレースホルダになっています。

2. 形を決める「設計図」

分割されたデータの2番目以降の要素は、アスキーアートを描くための「設計図データ」です。 ここには「空白を何個、文字を何個置くか」という情報が圧縮されて格納されています。

全体フローチャート:データ参照の切り替え

この仕組みを踏まえた上で、全体の処理フローを見てみましょう。

コマンドライン引数の
有無で条件分岐

引数なし

引数あり

コマンドライン引数の
有無で条件分岐

引数なし

引数あり

M3 Quine実行開始

分岐

M3 Quine出力開始
ステータス: 0

M3T Quine出力開始
ステータス: 1

M3T Quine実行開始

分岐

ループ
i: 1,2,...,20

ループ
i: 21,22,...,40

行データiを元に
アスキーアート部分の
1行分の文字列生成

ループ終了

行データiを元に
アスキーアート部分の
1行分の文字列生成

ループ終了

テンプレート文字列取得

テンプレートから
Quine生成して出力

終了

モードによる参照データの変化

プログラム冒頭で決定される変数 n(M3モードの場合、引数なし=0, 引数あり=1)が、配列のどこを読むかを決定します。

  • テンプレートの取得: 常に l0番目 をデコードして使用。
  • 設計図(M3のすがた): n=0 の時は、l1〜20番目 をデコードして使用。
  • 設計図(M3Tのすがた): n=1 の時は、l21〜40番目 をデコードして使用。

このように、引数をスイッチとして「読むべき設計図のページ」を切り替えることで、同じコードから全く異なる形状を出力しています。

アスキーアート生成

最後に、デコードされた「設計図」を使って、どのようにアスキーアート(文字列)を組み立てているのか、その生成ロジックを見てみましょう。

奇数

偶数

開始

行データから
Byte列を取得

ループ
i: 1,2,..,{Byte列の要素数}

iの偶奇

i番目のByteの数だけ
aから出力文字列に追加

i番目のByteの数だけ
空白を出力文字列に追加

ループ終了

終了

ここでのポイントは、文字を描画する際の「インク」のような存在である変数 a です。 変更可能な変数として a にアスキーアート部分の文字列全体を格納しておき、設計図の指示に従って a から文字を順番に取り出しては並べるという処理を行っています。

  • 設計図「次は10文字」→ a から10文字取り出して追加
  • 設計図「次は13空白」→ を13追加
  • 設計図「次は22文字」→ a から22文字取り出して追加

また、この設計図データにはある重要な法則があります。 それは、1行分のByte列(設計図)の数値を合計すると、必ず「100」になるという点です。

「10(文字) + 13(空白) + 22(文字) + ...」のように数値を足していくと、M3モードでもM3Tモードでも、どの行も必ず合計が100になります。 これはアスキーアートの1行の文字数(幅)と一致しており、この制約を守ることで、出力されるコードが綺麗な長方形のブロック形状を維持できるようになっています。

おわりに

Quineの解読はこれで終了です。

今回解説したレイアウト手法ですが、過去に作成されたM3 Quineとは全く異なるアプローチを採用しています。 もし興味があれば、他のQuine解説記事もぜひ覗いてみてください。アプローチの違いを楽しんでいただけるはずです。

直近の過去記事はこちらです。

Quineには「正解」がありません。言語仕様の隙間を縫って、自分だけの表現方法を見つけるプロセスこそが醍醐味です。 皆さんもぜひ、オリジナルのQuine作りに挑戦してみてはいかがでしょうか?

We are hiring !!

最後に、このQuineのコード末尾に隠されたメッセージを紹介します。

))/*Get*/.joined/*in,*/()}/*Stay*/.joined/*to the mission,*/())}/*Create*/.joined/*innovation!*/()))
//===================== We are hiring !! : https://jobs.m3.com/engineer/ =========================//

配列を連結するSwiftのメソッド .joined の間にコメントを刻み、"Get joined in, Stay joined to the mission, Create joined innovation!" という一文を作りました。日本語に訳すなら、「仲間になり、ミッションを共有し、共にイノベーションを創り出そう!」 といったところでしょうか。

エムスリーではイノベーションを .joined してくれるエンジニアを絶賛募集中です。 このQuineのような技術の無駄遣い(?)を愛しつつも、本番にデプロイするコードは真剣に書く。そんなギークでスマートな方のご応募をお待ちしています。

jobs.m3.com




以上の内容はhttps://www.m3tech.blog/entry/swift-quineより取得しました。
このページはhttp://font.textar.tv/のウェブフォントを使用してます

不具合報告/要望等はこちらへお願いします。
モバイルやる夫Viewer Ver0.14