ICFPC 2016

今年は 4 年ぶりの単独参加。実は最近オレオレ言語、それもいわゆる日本語プログラミング言語を作っていて、今年は日本主催らしいし、それならばと半ば思いつきでその言語を使うことを決心。さすがに自分の趣味に他人を巻き込むわけにもいかないので、当然にして単独参加と決まったわけである。昨年のチームメートには声すらかけなかったのだけど、今になって思えば一言ぐらい言及してもよかったかなあ…。まあ固定のチームというわけではないので気にしないことにする。

もともとまともな戦いをするつもりはなかったが、実態は予想以上に困難であった。何と言ったって機能が足りない!この日のために準備(=開発)をしてきたとはいえ使える時間には限りがあり、しかも一週間前に重大な問題が発覚して方針変更を余儀なくされたため、機能の一部は未実装だったり、実装しても試しに動かすことすらしていない状態のまま挑まざるを得なかった。コンテスト中に実装するまで this とか true/false に対応する単語はなかったし(ひどい)、コンストラクターを定義する手段は今でもない(本当にひどい)。もっとも後者はデフォルトコンストラクター(自動生成されるやつ)とクラスメソッドの組み合わせで何とかなってしまった*1という事情がある。ほかにもコンテスト中に処理系に加えた変更はこれだけ存在する

そんな状況だから、REST の API を叩いて、問題を一通りダウンロードするツールを書くだけで最初の 12 時間を使い切るなど、とても解法にまで手が回りそうな雰囲気はなかった。一方、審判の発言を素直に聞いて、適当に人手で問題を作ってアップロードしていたのだが、これがまるで解かれず、実質的にコードを 1 バイトも書かずして途中経過で 20 位前後まで浮上するという怪奇現象を見ることになる。最終的にはそれなりに解かれて点数も順位も下がったが、なお点数が 5 桁である可能性は極めて高い。

さらに、追い打ちをかけるように、ユーザー定義型を汎用型のパラメーターとして渡すとコンパイラーが謎の場所で落ちるというトラブルにも見舞われた(例:「頂点型の リスト型」*2)。これは、雑に言えば、コンパイル中はユーザー定義型は型として不完全な状態にあることに起因している(と思われる)のだが、頂点と多角形にあたる型をそれぞれ別のコンパイル単位で定義する(アセンブリを別にする)という逃げ道に気づかなければ自作言語の使用を諦めざるを得なかった可能性もあり、かなり危険な状況であった。

終了まで 16 時間を切った頃、ようやく「手で解いた手順をプログラムとして記述すると解答を出力してくれる」という現実的な着地点を見出し、そこで一気にモチベーションが上がる。最終的には徹夜してコンテスト終了まで完走するはめになったが、目的はほぼ達成された。以下はそのプログラムからの抜粋。

問題8を解くとは メソッド
  折り紙を準備する
  「1,0」の 座標から 「0,0」の 座標に 折り紙を 折る
  「0,1」の 座標から 「0,0」の 座標に 折り紙を 折る

大事なことなので 2 回言うが、これはれっきとしたプログラム(の一部)である。まあ、百戦錬磨の読者諸氏からみれば鉤括弧なり空白なりが不自然だからわざわざ言う必要もないか。どちらにせよ、プログラムをコンパイルしてこのメソッドを実行すれば「折り紙」の内部状態が正しく変化する。

このように、コンテストという観点からすれば単独参加+独自言語という事情を勘案してもなおお粗末であったと言わざるを得ないが、言語設計に関してさまざまな知見が得られたのは有意義であった。正直、2 日目の夜頃にはこの言語は投げ捨てたほうがいいのではないかと思い始めていた。たとえば、二直線の交点を求めるメソッドの定義はこんな感じである。

※ <相手>と <自分>の 交点 → <座標>
※ ax+by−c=0
交点は メソッド
  引数は 直線型
  戻り値は 座標型

  (引数を) 相手と 名付ける
  相手の aと 自分の bの 積から 相手の bと 自分の aの 積を 引いて
    約分して 分母と 名付ける
  点Pは 座標型の 変数
  相手の cと 自分の bの 積から 相手の bと 自分の cの 積を 引いて
    分母で 割って 約分して 点Pの X座標に 入れる
  相手の aと 自分の cの 積から 相手の cと 自分の aの 積を 引いて
    分母で 割って 約分して 点Pの Y座標に 入れる
  点Pを 返す

こんなコードばかり書いていたのでは嫌にもなる。

しかし、垂直二等分線を求めるメソッドが必要だと気づいてコードを書いたところ、最終的には次のようなものになった。

※ <点>と <点>の 垂直二等分線 → <直線>
垂直二等分線は クラスメソッド
  引数は 座標型、座標型
  戻り値は 直線型

  (第二引数を) 点Qと 名付ける
  (第一引数を) 点Pと 名付ける
  点Pと 点Qの 中点をとおり 点Pと 点Qを 通る直線に 直交する直線

引数をスタックから一時変数に移しているのがダサいのは置いておくことにして、一番下の行を読んで欲しい。ブログラミングを知らなくたって、日本語話者で中学・高校レベルの数学ができれば、これはいかにも垂直二等分線を求めようとしていると一目見ただけでわかる。これでもちゃんと動くんだよ!このとき僕はこの言語のポテンシャルを垣間見たわけである。設計の一部は再考の必要があるが、Mind(もとにした言語)のよさはちゃんと息づいていた。

参戦記が思ったより長くなってしまったので、言語自体の細かい話は別の機会に譲ることにする。

*1:これはれっきとした Factory パターンなのでそんなに悪くなかったりする。

*2:C# の List<頂点型> にあたる。