かなり久しぶりのブログです。
4~5年ほど前から書き捨てのコードや勉強用のコードなどを一つのリポジトリに雑に放り込んでいたのですが、それなりのサイズになってきたので今まで書いてきたコードを振り返ってみることにしてみました。
対象となるコード
対象となるコードは、前述した通りのプライベートリポジトリの中にあるもの全てになります。このリポジトリ自体は(本の写経など権利的に公開してよいか微妙なものもあるので)非公開ですが、だいたい以下のような目的で書かれたコードが放り込まれています:
- 本やドキュメントなどのサンプルコードの写経やそれをもとに書いてみたコード
- 特定のライブラリ or API or 言語機能等をとりあえず使ってみるだけのコード
- 書き捨ての、おそらく二度と使わないコード
- 一旦GitHub等で公開してみたものの、そんなにメンテする気も使う気もないので消してしまったものを墓場がわりにsubtreeでぶち込んでいるもの
- 等々……
その他にも、この前実家に帰ったタイミングで発掘された過去のコード(おそらく2009~2017頃に書かれたもの)も一部含まれています。さすがにこの時代のコードは大部分が消失してしまっていますが、それでもある程度主なものは奇跡的に残っていました。
集計
こんな感じのRubyスクリプトを書いて、どの言語をどれくらい書いたのか集計してみます:
#!/usr/bin/env ruby
require 'rugged'
require 'linguist'
# YAML が統計にカウントされないのを無理やり直す (Kubernetes マニフェストとかもカウントされてほしい)
Linguist::Language.find_by_name('YAML').instance_eval { @type = :markup }
repo = Rugged::Repository.new('.')
project = Linguist::Repository.new(repo, repo.head.target_id)
lines = {}
total_lines = 0
pwd = Dir.pwd
project.breakdown_by_file.each do |lang, files|
lines[lang] ||= 0
files.each do |file|
blob = Linguist::FileBlob.new(file, pwd)
lines[lang] += blob.loc
total_lines += blob.loc
end
end
langs = project.languages.keys.sort { |a, b| lines[b] <=> lines[a] }
printf "%15s\t%8s\t%6s\n", "LANG", "LOC", "LOC%"
langs.each do |lang|
loc = lines[lang]
printf "%15s\t%8d\t%6.2f\n", lang, loc, (100 * loc.to_f / total_lines)
end
printf "%15s\t%8d\t%6.2f\n", "TOTAL", total_lines, 100.0
リポジトリのサイズがそれなりにでかいのでやや時間がかかりますが、「並列に集計しておけばよかったな~」と言っている間に集計が終わる程度には時間がかからなかったので今回はそのままにしました。
これを上述したリポジトリ直下で実行すると、以下のようにそのリポジトリに含まれているコードの行数が言語ごとに集計されます:
LANG LOC LOC%
Rust 49649 19.27
Haskell 30464 11.82
Go 30441 11.81
C 24654 9.57
YAML 16135 6.26
Python 15563 6.04
Scheme 10625 4.12
Java 9937 3.86
Assembly 8187 3.18
Scala 6221 2.41
Elixir 5133 1.99
Ruby 5067 1.97
Starlark 4573 1.77
OCaml 3787 1.47
C# 3539 1.37
Elm 3444 1.34
JavaScript 3378 1.31
HTML 3240 1.26
HCL 3048 1.18
C++ 3003 1.17
SystemVerilog 2396 0.93
Coq 2201 0.85
Cuda 2042 0.79
CSS 1480 0.57
Shell 1324 0.51
Makefile 1038 0.40
Lua 902 0.35
WebAssembly 891 0.35
Racket 887 0.34
Processing 699 0.27
Verilog 629 0.24
SuperCollider 523 0.20
CUE 520 0.20
Dockerfile 462 0.18
Procfile 457 0.18
Prolog 274 0.11
ShaderLab 205 0.08
PHP 189 0.07
Perl 111 0.04
CoffeeScript 105 0.04
M4 71 0.03
Batchfile 62 0.02
COBOL 59 0.02
Smarty 42 0.02
Dhall 23 0.01
Jsonnet 10 0.00
SCSS 9 0.00
Brainfuck 1 0.00
TOTAL 257700 100.00
思っていたより言語の数が多かったので、コード量上位10件分くらいの言語について何を書いたのか等を振り返っていきたいと思います。
第1位: Rust (49649行)
1位はRustでした。とはいえ、体感としては行数ほど習熟している感じはあまりしません。 書いたコードはおそらくほとんど今年のものだと思います。また、実用的なコードをちゃんと書いたこともありません。
行数が膨れ上がっている理由は、おそらく今年の夏ごろに転職しようと思ってLeetCodeやAtCoderの問題をちょこちょこ解いていたからだと思います。雑に数えてみると、この2つだけで合計255問も解いていたようです(キリがいいのは偶然です)。この手のコード(特にAtCoderのもの)はテンプレ関数のコピペによって行数が膨れ上がる傾向にあるので、そのあたりがこの結果につながったのではないかなと思います。
その他にも最近は ev3dev-lang-rust でマインドストームのロボットを動かしたりするのにもRustを使っています。
第2位: Haskell (30464行)
2位はHaskellでした。Haskellはここ数年ほとんど書いていないので、おそらく大昔に書かれたコードが大半を占めているのではないかと思います。 内容としては、主に高校~大学(2012~2016くらい)の頃に書かれた中規模程度のアプリケーションのコードが多いように見えました(逆に書き捨てのコード系は古いのでほとんど消失してしまっているようです)。
具体的なコードとしては、Yesodで作られたWebサイト、大学の講義で作ったCのサブセットのコンパイラ、Git(のかなり限られたサブセット)のクローン実装、諸々のesolangの自作処理系等が眠っていました。
Haskellは最近は全然触っていませんが、並行処理を低コストかつ気軽に行うことができる結構いいランタイムを持った言語なので現代においてこそ役に立つ場面がまだまだあるんじゃないかという気がしています。またどこかの機会で触っておきたいです。
第3位: Go (30441行)
3位はGoでした。Goはここ3年程度メインで使っていたので、これだけ行数が増えるのも納得かなという感じです。
内容についてですが、先ほどのRustと違ってこちらは何かのライブラリなどをとりあえず試しに使ってみただけみたいなコードが大半を占めているように見えました。先ほどと違って言語の習熟度に比例して順当に行数が増えている感じもあり、納得感があります。
Goの行数が多いその他の理由として、最近は何の言語でやってもいいことはとりあえずGoでやることが多かった点、コード生成を多用する文化なので生成されたコードがある程度混ざってそうという点も挙げられます。
第4位: C (24654行)
4位はCでした。CのコードもGoと同様に勉強用っぽいコードがほとんどを占めていますが、Goとは違って特定のライブラリというよりはPOSIXのAPIを叩いてみるだけなどのようなコードが多いように見えました。
中学生の頃(2009頃?)、自分が一番最初にプログラミングを勉強し始めた時期のコードもごく一部残っていました(おそらくこの時代としては珍しく、なぜかCでプログラミング入門しました)。この頃のコードはSVNで管理されていたり、そもそもVCSを使わずに日付でディレクトリを切って†バージョン管理†しているコード等もあって懐かしい気持ちになりました。
最近はなぜか一生使うことのなさそうなWindows APIを触り始めていたりするので、Cの行数はこれからも増えていきそうです。
第5位: YAML (16135行)
5位はYAMLでした。これはおそらくほとんどKubernetesのせいだと思います。それ以外にも諸々のツールやフレームワークなどの設定ファイルなども若干含まれているようです。
Kubernetesは2020年ごろにちゃんと触り始めましたが、2021年の途中くらいから仕事が忙しくなりすぎてキャッチアップする暇がなくなってきてしまいました。そのため、最近の事情などはあんまりわかっていません。また、今のところユーザーとしての立場でKubernetesを触ることしかしていなかったので、今後は自宅にクラスタを立てて管理してみるというのも面白いかもしれません。
第6位: Python (15563行)
6位はPythonでした。Pythonは最近はあまり触っていませんが、昔(中高生くらい。2009~2012頃?)はメインで使っていたためそれなりの量のコードが残っていました。現在残されているコードも多くはこの時代に書かれたもののようです。
そのほかにも、研究室(2017頃)でちょっとだけディープラーニングをかじっていた頃のコードも少し残っていました。
最近はごく稀に Pyxel でちょっとしたミニゲームを作るのにPythonを使ったりしてます。
第7位: Scheme (10625行)
7位はSchemeでした。なぜか2016~2017ごろにGaucheばかり書いていた時期があったので、この頃のコードが大半なのではないかと思います。それ以外にも授業で JAKLD というマイナーScheme処理系を使って課題を解いていた頃のコードなどもごくごく一部だけ残っていました。Linguist gemの判定では別言語扱いになっていますが、Racketのコードも少しあります。
第8位: Java (9937行)
8位はJavaでした。見事ランクインしたJavaですが、実態としては2016年頃に大学で出されたHTTPサーバーを作ろうみたいな課題を提出するためにたった一度書いたっきり、二度と触れることのないまま今に至っています…。
本来はおそらく200行程度で済む簡易的なHTTPサーバーが一個あれば課題としては合格だったはずなのですが、気合を入れすぎて機能モリモリのWebアプリケーションフレームワークもどきをフルスクラッチで作ってしまったので行数が大幅に増えてしまった上に提出期限もだいぶ過ぎてしまった記憶が残っています。今思うと、採点する側も大変だっただろうな……。
とはいえ、まあ使える程度のルーティング、テンプレート、ORM(もどき)がぜんぶ入ったWebアプリケーションフレームワークであっても意外とこの程度の規模に収まるんだなということに気づけたのは結構いい経験だったのではないかなと思います。
第9位: アセンブリ (8187行)
9位はアセンブリでした。内訳としてはほとんどがx64のものになります。それ以外にも、少数ARMやRISC-V, MIPS, 大学で作った謎の自作アーキテクチャなどのものも混じっています。
アセンブリについてはあまり具体的に何かを作ったという感じではなく、CやGoと同様に勉強目的で何か動かしてみただけみたいなコードが大半を占めています。 そのうちアセンブリがちゃんと必要になってくる物も作りたいですね。
アセンブリの行数が多い理由は、たくさん書いたからというよりは単純にアセンブリだからだと思います。
第10位: Scala (6221行)
10位はScalaでした。内訳としては自作Schemeインタプリタが半分、 RISC-V本 を参考に書いたコードが残り半分といった感じです。
Scalaは2014年ごろバイトで使っていたのですが、バイトを辞めてからはほとんど自主的に書かなくなってしまいました。
まとめ
書き捨てのコードだけで25万行以上も溜まっていたのにはびっくりしましたが、よく考えると13年分のコードが(一部とは言え)積み重なっているわけなので一日に54行書けば今の行数に達してしまいます。 実際は直近4年くらいのコードが多くを占めているのでこんなに単純な計算にはならないのですが、それでも塵も積もれば結構な行数になるということが実感できました。
また、単純なコードの行数と主観的な習熟度に対する自信がそんなに釣り合っていないというのも新しい気づきでした。
来年は仕事ではGo, 趣味ではRust, C, アセンブリみたいな年になると予想しているので、今後はこのあたりの言語の行数がまた伸びていくんじゃないかと思っています。
それでは、よいお年を。