Magnolia Tech

いつもコードのことばかり考えている人のために。

どうやってコーディングを学ぶか

自分が学んだ頃の、時代的なものもあるけど、今でもPerlのモジュールは粒度が小さく、ドキュメント、テストがしっかり用意されているので、参考にするにはちょうど良いと思っている。

とはいえ、今更新しくPerlを学ぶか?という話は有るので、身近な人が教えてくれる言語をまずは選んだ方がいい。


あと、現代的な言語を学ぶ上では、その言語で使えるテスティングフレームワークをなるべく早く覚えると良い。アサーションで書く、「どう動いて欲しいか」という意図が明示的に書けるようになった段階で、コーディングを学んだと言えると思う。

最初の頃って、自分でも「どう動いて欲しいのか」という所があいまいなまま書いていることが有って、動いてみて初めて「あぁ、これだ」と思うことが有ったのだけど、今なら早いうちからテストを書くことでそこに近づけると思う。

おすすめは「テスト駆動開発」に出てくるお題を、書籍で取り扱っているJavaPython以外の言語で試してみること。

テスト駆動開発

テスト駆動開発

最初は驚くくらい書けない。

写経と、自分でゼロから書くのでは全然違うので、まずその感覚が比較的短いコードで理解できる。

分からなくても、とりあえず元のお題の通りにJavaPythonで動かしてみれば、「どう動くのが意図通りなのか」という正解がすぐに分かる。


そこで慣れてきたら、やはり他の言語で書かれたプログラムを移植したり、ログのフィルタとか、JSONの単純なパーサーみたいに比較的小さなコードで実現できるものを書くといいと思う。

その先は、たくさんコードを読んで、たくさん書いて、ときどき良いプラクティスが書かれた本を読んで、またコードを書いて…の繰り返し。

例えばOSSのissueを勝手に解決するPRをどんどん書いてみるとかも良い。特に作りたいものが思い浮かばなくても生きたお題がそこにたくさん用意されているのだから。

sbt 1.4からサポートされたBSP(Build Server Protocol)について

sbtを1.4にアップグレードすると、.bspという見慣れないディレクトリが作成されるようになったことに気づいた。

今更だけど、なんだっけ?と思って、sbt 1.4.0のリリースノートを見たらちゃんと書かれていた。

https://github.com/sbt/sbt/releases/tag/v1.4.0

このバージョンからBSP(Build Server Protocol)がサポートされ、sbtを起動すると.bsp/sbt.jsonというファイルが作られると書かれている。

なるほど

BSP(Build Server Protocol)とは?

https://build-server-protocol.github.io/

BSPは、Scala CenterJetBrainsが策定した、IDEやエディタからビルドツールをコントロールし、ビルドやテスト、デバッグなどを実行するためのプロトコル

このプロトコルの標準化により、共通のインタフェースに基づいて相互に実装すれば相互運用性が高まる、ということを目標にしている。IDEやエディタが個別にビルドツールごとの機能を個別にサポートしているとできることに差異が生まれたり、サポートの進捗が違ったりしてしまうが、この仕組みであればそのようなことが起こりにくくなる(起きないとは言えない)。

ちょうどマイクロソフトプログラミング言語と、IDE/エディタを繋ぐためにLSP(Language Server Protocol)というプロトコルを策定したのと同じような関係にあり、LSPが主にコーディング時のサポートを目的としていたのに対し、BSPはビルドやテストをサポートする、という違いがある(上記のサイトにも、BSPは、'LSP-inspired'だと書かれている)。

sbt以外では、bloopというツールがBSPをサポートしている。

https://scalacenter.github.io/bloop/

使い方

詳しくはそれぞれの公式サイトを見た方がいいので、割愛する。

いずれにしても先にsbtをbsp用に立ち上げておく(sbt -bsp)という手順が必要になるので、注意。 metalsの場合は事前に起動していなくても大丈夫とコメントで指摘いただきました。

metals

https://scalameta.org/metals/blog/2020/11/06/sbt-BSP-support.html

IntelliJ

https://www.jetbrains.com/help/idea/bsp-support.html

バージョン管理の注意点

バージョン管理時の注意点として、.bsp/sbt.jsonはマシン固有のパス情報が入っているファイルなので、バージョン管理の対象するべきではない。しかし、sbt 1.4以降ではBSPを使うか否かに関わらず必ず作成される。

今後は、sbtを使うプロジェクトでは、.gitignoreに「.bsp/」を追加しておいた方が良い。

おわりに

まだScala界隈でしか使われていないBSPだけど、ひょっとしたらビルドツールを超えて普及していくかもしれない。

関心量について

関心量は有限だし、必ずしも”正しい”方向に向けられるとは限らないので、関心がどこに向くようにコントロールするかは、当事者の責任ではなく、環境を与える側の責任なのです。

『計算できるもの、計算できないもの』を読み始めた…計算ってなんだろう…計算機ってなんだろう

『計算できるもの、計算できないもの』が年末年始のおやすみに入る前に届き、けっこうな量のメモを取りながら読み進めていたけど、全然進まなかった。


昔、コンピュータサイエンスの授業を受けたとき、いきなり出てきた”オートマトン”や、”チューリングマシン”の解説が、それまで少し学んでいたプログラミングとか、目の前にある実際のコンピュータとどう関連するのか、全然分からなかったし、ましてやそれまで習ってきた四則演算みたいな「計算」とも全然違うもので、先生が言っている内容はロジカルだったけど、「なぜ今これを学んでいるんだっけ?」ってのが分からないまま過ぎて行った記憶が有る。


ある程度プログラミングの学習が進んでいくと、「そもそも計算って何だ?」「計算機って何だ?」みたいな疑問にいく時が有ると思うけど、この『計算できるもの、計算できないもの』はそれに少しだけ答えてくれる(気がする)。

冒頭で計算を「扱いやすい問題」「扱いにくい問題」「計算不能問題」の3つに分類し、まずは計算できない事例、計算問題のカテゴリや、各種問題の定義、定番のチューリングマシンの定義へ進む構成が、当時受けた授業より分かりやすかった。

そもそも日本語だと、computation、calculation、calculusとそれぞれ違う意味の単語を全部「計算」で済ませるから分からなくなる(ちなみに本書でいう計算は「computation」…⦅かたく⦆ (数学的)計算(法); 算定数値; コンピュータのによる処理.と辞書には書かれている)

たぶん、冒頭の、当時の自分は、個別の理論よりも「全体における位置付け」や「現実のコンピュータとの関わり」が知りたかったのだと思う。そうゆう意味では、知りたい順番に並んでいる本書を当時の自分に届けたいと思える構成だった。

さらに、個人的には、第6章の「万能コンピュータプログラム」、特に「6.3 実世界における万能計算」が一番分かりやすくて、色々な階層に万能計算できるものが存在する、という概念が最初に分かっていればもっと素直に理解できた気がする。多分当時は、チューリングマシンを、何か特定のVMみたいなものと捉えようとして混乱していたのかもしれない。実際のコンピュータが出現する前から存在する思考実験の題材である、と分かっていれば当時もっと良い成績が取れたに違いない :)

とはいえ、改めて「計算」について学び直そうと思い、年末年始の3日間くらいを使って集中的に読んだものの前半の流し読みくらいしかできなかったので、きっと一年くらいかけて読んでいかないといけない1冊なんだと覚悟した。

とにかく噛みごたえのある一冊です。流し読みだと全然頭に入ってこない。

誰か一緒に読んでください。


個人的には、よりコードの占める割合の多い『アンダースタンディング・コンピュテーション』の方を先に読んでおくと入りやすいかな、と感じた。ただ、『アンダースタンディング・コンピュテーション』もかなりの噛みごたえの有る本なので、そうそう簡単には読み終わらないのだけど(今回併せて読み返して、後半を全然読んで無いことに気づいて、こちらも読み直している)。

「普段なにげなくやっていること」を限界まで細かく分解してみる、”判断するポイント”を挙げてみる

これは練習しないとできないことだと思うので、意識的に一度やってみると良いと思っている。

昔、自分がやっているタスクが全然マニュアル化されていないことがふと気になって、なにげなく手順とか、「誰々からの連絡は例外的な手順に移す」といった判断ポイントを書き出したら、あっという間にA4用紙で10枚くらいの分量になって驚いたことがある。

書き出してみると気づくのだけど、普段意識しないまま判断していることがいかに多いか、公式な手順の間に”上手く行かせるための工夫”がたくさん挟まっているか、よく分かり自分の作業に意識的になれたし、また他の人にも伝えることができるようになって、結果的にスループットが上がって行った。

  • まずは頭から思いつく限り手順を挙げていってみる その際、何かにメモする、誰かに連絡する、システムに投入する…本当にその通りにやって普段と同じことが再現できるまで細かくする
  • 例外的な手順を書き出してみる ”なぜ”それが例外なのか、理由を書き出してみる
  • 公式な手順に含まれていないことをチェックしてみる それが公式な手順に含まれない理由を書き出してみる

言葉だけじゃなくて、図でもいいけど、必ず手を動かせるインプットになることを心がける。


マイケル ポランニーの『暗黙知の次元』では、「私たちは言葉にできるよりも多くのことを知ることができる。」と書かれていて、きっとこの方法でもまだ出てこない”手順”や、”基準”がもっともっと有るに違いない。

でもまずは挙げてみることが大事。

ただ、じゃあそれを全部マニュアル化すべきか?と言われると、それはそれでちょっと違う気もする。それより、自分がそれだけ膨大な手順や基準に基づいて行動している以上、他の人とそうそう簡単に同じことができるわけがない……で、その先でどうするか?考える……という捉え方の方がポジティブじゃないかと思っている。

フォークされたjuniversalchardetの挙動が変わって、US-ASCIIを判定するように変わっていた

長い人生、生きていれば文字コードの推論をやらねばならない場面が一度や二度は有るものだ(実際にはUTF-8統一時代になって、あまり発生しなくなったけど……)。

そんな時、JVM系言語であれば、juniversalchardetが候補に上がることもあるかもしれない。

長らくバージョンアップされていないな、と思っていたら、実はフォークされたバージョンが割とこまめにメンテナンスされていた。

https://github.com/albfernandez/juniversalchardet

使い方はREADMEを読めば分かるので割愛するけど、一つだけ気を付ける点が有る。元のjuniversalchardetUS-ASCIIをサポートしていない。つまり、US-ASCIIに相当する文字データを渡すと、判定不能で返却された(nullが返る)。

しかし、以下のコミットでUS-ASCIIが判定できるように変更が加えられている。

https://github.com/albfernandez/juniversalchardet/commit/fcf1898ab7bae3f7308d991e225c3016e460b352

つまり、US-ASCIIという文字列が返ってくる。返却値がnullの場合は、US-ASCIIと解釈し、UTF-8で上書きするようなコードを書いていると、挙動が変わってしまうので、注意。

以下のコードで確かめた。

package example

import org.mozilla.universalchardet.UniversalDetector

object Detector {
  def main(argv: Array[String]): Unit = {
    val detector = new UniversalDetector(null)

    val str = "This is Love"
    var bytes = str.getBytes()
      detector.handleData(bytes, 0, bytes.length)


    detector.dataEnd()

    val encoding = detector.getDetectedCharset()
    if (encoding != null) {
      println("Detected encoding = " + encoding);
    } else {
      println("No encoding detected.");
    }

    detector.reset()
  }
}
ThisBuild / scalaVersion     := "2.13.4"
ThisBuild / version          := "0.1.0-SNAPSHOT"
ThisBuild / organization     := "com.example"
ThisBuild / organizationName := "example"

lazy val root = (project in file("."))
  .settings(
    name := "Scala Seed Project",
    libraryDependencies ++= Seq(
      "com.googlecode.juniversalchardet" % "juniversalchardet"  % "1.0.3",
//      "com.github.albfernandez" %  "juniversalchardet"          % "2.4.0",
    )
  )

フォークされたバージョンが必ずしも元のバージョンと同じ挙動とは限らない点は、注意しないと(そもそも挙動を変える必要がなければフォークもしないと思うけど)

ケーブルを収納しておくためにはビニールケースや、ジップロックが良い

早速手元にスライドして閉じられる厚めのビニールケースが有ったので、それに入れてみたら中身も分かるし、立てかけられるし、ケーブルが汚い感じで絡まることもないし、良い。

ケーブルの種類ごとに分けて、規格を書いておくと便利っぽい。

ジップロックも単価が安いので量を使うときはそっちがいいかも。