かなり久しぶりのブログです。

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, アセンブリみたいな年になると予想しているので、今後はこのあたりの言語の行数がまた伸びていくんじゃないかと思っています。

それでは、よいお年を。