はじめに
この記事ではGoで書いた競技プログラミング向けのコードバンドラ「go-bundler」について紹介します。
https://github.com/Atnuhs/go-bundler
AtCoderをGoでやるために
AtCoderでは、Goの提出は1ファイルにまとめる必要があります。競プロ用のライブラリを複数ファイルや複数パッケージに分割して利用する場合、その差を埋めるための手順やツールが欲しくなってきます。
既に誰か作っていないか調べましたが、未完成だったり、出力の正しさやジェネリクス対応がはっきりしなかったので、Goの公式ツールとして提供されているbundleコマンドを利用していました。
bundleは、あるGoパッケージを単一のソースファイルに展開し、別のパッケージから取り込める形に変換するものです。自分は、以下のように利用していました。
bundle -o ../submit/main.go . \
&& sed -i 's/main_main/main/' ../submit/main.go \
&& go run ../submit/main.go
この方法でも提出自体は可能でしたが、以下の課題が残りました。
-
mainパッケージ配下にライブラリのコードをすべて置く必要がある- パッケージ分割ができない
- 問題を解く際に、未エクスポートの関数や型も補完候補に並ぶ
- バンドル対象がパッケージ内の全コード
- その解答で使用していないコードもバンドルされるため、AtCoderの提出コードのバイト数が大きくなり、見栄えが悪い
- 環境にも悪い
- ツールの制約と挙動
- 自分の利用範囲では、埋め込み型とジェネリクスを組み合わせたコードで期待どおりに動かないケースがあった
ということで、AtCoderでGoを使う際の利用形態に合わせたバンドラを作りたくなったので作りました。
利用方法
インストール
go install github.com/Atnuhs/go-bundler@latest
利用方法
go-bundler -dir ./cmd/abc123 > submit.go
-dirは解答を書いたmain.goがいるディレクトリを指定すれば、だいたい何とかなります。頑張ってください。
取れる構成の例
以下のような構成を考えます。
.
├── cmd
│ └── abc123
│ └── main.go
└── internal
├── graph
│ └── graph.go
└── math
└── modint.go
cmd/abc123/main.goでinternal/graphやinternal/mathを使ったり使わなかったりして解答を書きます。問題を提出したくなったら以下のようにやります。
go-bundler -dir ./cmd/abc123 > submit.go
標準出力に出力されるので、clipboardなどを利用する場合はパイプでつないでやればよいです。そして各々の方法で各々の解法が提出されてゆくこととなります。
まとめ
書いてみた感想としては、astやtypes、rtaのあたりを初めて書いたので、ChatGPTに質問しつつ、構文木をプリントして確認する方法とか身についたかなと思います。rtaやssaはブラックボックス化されており、使うだけだったので楽でした。あとはパッケージの依存関係の表現はDFSによるトポロジカルソートを利用できてよかったです。基本的な処理構成としてもmapでメモ化しつつ木を走査していく、という流れになっており競プロ要素かもしれま解釈によってはせん。構文木を扱うコードは普段書く競・業プロのコードと一味違って上手く処理をまとめるのが難しかったです。
最後になりますが、ほかの人はどうやって提出しているんでしょうか。気になるのでどなたかコメントで教えてください。
そんなところです。