空想犬猫記

※当日記では、犬も猫も空想も扱っておりません。(旧・エト記)

Beethoven Sonata # 14 "Moonlight" Op. 27 No. 2

先ずは第一楽章から。

2週間くらいで大体音は出せるようになるが、旋律の輪郭の描き方は無限にあるような気がしていて、弾いていて飽きない。

www.youtube.com

第二楽章、変ロ長調スケルツォ形式。フラットが5つ付いた楽譜を譜読みをして鍵盤上の動作に変換する場合、まだリアルタイムに処理することができないため、自分にとっては一番難解。どうやって頭の中で符号化して暗譜するかを模索中

第三楽章、一般的には一番難しく聴こえるけれども、音階の繰り返しなので、暗譜はそれほど問題ではないかも。指は毎日弾いていれば動くようになるはず。あと4か月くらい練習すれば弾けるようになる、鴨。

Chopin - Largo in E flat major , Op. posth.

ショパン 2 曲目は、昨年のショパンコンクールで反田さんも弾いていた Largo in E flag major。今の自分の楽譜の処理能力が追いつくと思われたので挑戦。

www.youtube.com

最初の Prelude Opus 28, No 4 が左手の和音の練習だとしたら、この曲は右手の旋律にも和音が加わる。最初の2音目で既に重厚な和音がグワッと響いて泣きそうになる。曲と言えそうな曲はブルグミュラー師の練習曲しか弾いたことがないので、新しい和音の、ピアノから出る生音の響きが新鮮である。

Chopin - Prelude Opus 28, No 4

ピアノを始めた動機のひとつは、ショパンの曲を弾きたいというものだった。そのための最短経路として選んだのがブルグミュラーの25、そして18の練習曲であった。未だに18の練習曲の最後の曲は完成していないものの、少しずつ弾きたかった曲に挑戦していこうと思っている。Largo で、自分の譜読みの速度でも初見である程度弾けそうな感触だったので、ショパンの Prelude Opus 28, No 4 を最初に選曲した。

www.youtube.com

私のレベルで気をつけたのは

  • 左手の3ないし4和音を1つの音として(各音がずれないように)弾くこと
  • 左手の音量は右手の旋律よりも遥かに小さくすること
  • 和音が切り替わる際に、音の連続性を保ちつつ、濁りを最小に抑えるように sustain pedal のタイミングを意識すること

ゆっくり丁寧に弾くことで、和音の連打の良い練習になると思われる。

Rust独学メモ3

テストの書き方

Unit テストと Integration テストで、書く場所が異なる

We create a tests directory at the top level of our project directory, next to src. Cargo knows to look for integration test files in this directory. We can then make as many test files as we want, and Cargo will compile each of the files as an individual crate.

  • Unit テストはモジュール内に、#[cfg(test)] というアノテーションを付けたモジュールを作成する
    • Unit テスト用のモジュールからは、publish されていない private なメンバにもアクセスできる
    • 関数に #[test] アノテーションを付けておくと test runner にピックアップされて実行される
  • Integration テストは src ディレクトリの並びの tests ディレクトリに、.rs ファイル(=クレート・ルート)を作成する

Integration テストのサブモジュールを .rs 形式で作ると、クレートとしても、サブモジュールとしても参照されそうだなーというのが第一感。それの疑問に対する回答は

Having common appear in the test results with running 0 tests displayed for it is not what we wanted. We just wanted to share some code with the other integration test files.

To avoid having common appear in the test output, instead of creating tests/common.rs, we’ll create tests/common/mod.rs.

まさかのノーガード戦法だった。しかもここだけなぜ「running 0 tests」が出てしまうのかの説明を省いてリンクを張ってお茶を濁していて誠意を感じない。クレートとモジュールの恣意的な扱い(おそらくコンパイラの実装の都合)のしわ寄せがここに来て、足の小指をぶつけてる。要は運用で回避してねという方針。結論:クレートとしても、サブモジュールとしても参照される。そして、インテグレーションテスト用の .rs ファイルで書かれたサブモジュールは、サブモジュールを利用する .rs ファイルの数の回数分、再コンパイルされてしまう。これは、設計者からしたら誤用の範囲であると思うが、サブモジュールとして利用された .rs ファイルに書かれたテストは、サブモジュールとしてインポートされた回数と、自分自身がクレートとしてコンパイルされた+1回だけ実行される。インテグレーションテストを書いていて、とあるファイルに書いたユーティリティをインポートして使うというのは、やりがちなことなので、意外と侮れない落とし穴だと思う。例えば、ひとつのファイルにユーティリティとテストを書き溜めて、サイズが大きくなってきたので、新しいファイルを追加して、元のファイルをインポートしてヨシ!続きを書くぞー…なんてことをすると、一気に流れるテストの数が2倍になるわけである。

知らなくてハマったら発狂しそうな仕様である。

Rust独学メモ - 空想犬猫記 できっちりとクレートはコンパイル単位、ファイルはモジュール、コンパイラに最初に渡されるファイルがクレート・ルートで、それがクレートのルートモジュールになり、以下芋づる式にサブモジュールがコンパイルされていく、という理解を固めて置かないとかなり置いてきぼりになりそう。現実的な解決策を出している点では文句の付けようは無いのだけれど。

Rust独学メモ2

モジュール

Rustにおけるモジュールは、C++における名前空間のようなもの。アクセス指定子を付けることによって公開・非公開をコントロールできる。

Start from the crate root: When compiling a crate, the compiler first looks in the crate root file (usually src/lib.rs for a library crate or src/main.rs for a binary crate) for code to compile.

コンパイルはクレート・ルートから始まり、クレート内の階層化されたモジュールの宣言はこのファイルに書く。

Declaring modules: In the crate root file, you can declare new modules; say, you declare a “garden” module with mod garden;. The compiler will look for the module’s code in these places:

  • Inline, within curly brackets that replace the semicolon following mod garden
  • In the file src/garden.rs
  • In the file src/garden/mod.rs

モジュールの定義の仕方は1+2=3通りから選べる

  • mod garden { ... } のようにモジュール宣言の直後に括弧でスコープ作って、インラインで定義する方法
  • あるいは、mod garden; のように、forward declaration した場合、コンパイラ
    • モジュールと同名の .rs ファイル(src/garden.rs )か、
    • モジュールと同名のフォルダ内の mod.rs ファイル(src/garden/mod.rs)に、定義を探しに行く

⇒ Rust のソースファイルは1つのモジュールに対応している(大胆!)
⇒ モジュールとして提供する機能が複数ファイルに及ぶような場合はサブモジュールを作って、use キーワードでインポートする運用(つらい)

クレート・ルート以外のモジュール内でも同様にサブモジュールを定義出来て、

  • mod vegetables { ... } のように、モジュール内にインラインで書くか
  • サブモジュールと同名の .rs ファイルをサブフォルダに置く(src/garden/vegetables.rs)か、
  • サブモジュールと同名のサブフォルダのサブフォルダに mod.rs を置く(src/garden/vegetables/mod.rs)とのこと

⇒ ファイル・ディレクトリの構造以上にモジュールの構造が散らからない。クレート全体がコンパイル単位なので、クレート内では自由に相互依存ができる。スコープ演算子によるモジュールパスの解決、pub、use は、特筆することはなさそう

まとめ
  • Rust のモジュール階層は、クレート・ルートから芋づる式に定義される
  • ファイル、あるいはフォルダ + mod.rs を見たら、それがモジュールの定義
  • use を使って適度にモジュールを統合しないと :: だらけの悲惨なコードになりそう。どれくらい :: を使うか、どのように使わせるかという塩加減は、おそらく長期的には重要なスキルになると思う
感想

1ファイルをモジュールに対応させたり、root のモジュールを crate と呼んだり、言語の内部実装に利用者が付き合わされて(振り回されて)いる感じがするものの、インクルードパスとか気にしなくても良いし、実用には耐えそう。

Rust独学メモ

PackageとCrate

最初にRust入門した時には、cargo newして、コードを実装して、テストを書いて、何となく動いたわーい、って誰しもやると思う。でもある程度構造化されたプログラムを組もうとすると

ユーザ定義型・メソッド・関数 < モジュール階層 < クレート < パッケージ

という風に階層的に管理する必要が出てくる。モジュールの階層は、ディレクトリ構造に対応しているので分かりやすいが、クレートとパッケージはどのような関係にあるのだろう。また最初に cargo new して作るものが一体何なのか、少しわかりにくいのではないだろうか。The Bookによると

A crate is the smallest amount of code that the Rust compiler considers at a time.

クレートとはRustコンパイラが一度に考慮するコードの最小単位である ⇒ コンパイル単位であるということか。ライブラリ全体がコンパイル単位なので、forward declaration のようなことをしなくてよいのだろうと推測

A crate can come in one of two forms: a binary crate or a library crate.

クレートにはバイナリと、ライブラリ・クレートの2種類 ⇒ OK
でもって、バイナリには main 関数がある。まあいいでしょう。

The crate root is a source file that the Rust compiler starts from and makes up the root module of your crate

クレート・ルート、はソースファイルで、ルート・モジュールを構成する起点となる。(なんか新しい概念が2つ提示された。詳しくは後で、だと)

A package is a bundle of one or more crates(中略)A package contains a Cargo.toml file that describes how to build those crates.

パッケージは1つ以上のクレートを束ねたもの ⇒ OK。最初に提示した図の通り
パッケージは Cargo.toml ファイルを含む ⇒ なるほど、Cargo.toml を見たら、そこにパッケージがあると思えばよい

さらに、続いて

A package can contain as many binary crates as you like, but at most only one library crate.

パッケージ内には好きな数だけバイナリクレートを作ることができる。でもライブラリクレートは最大1個 ⇒ バンドルと言いつつライブラリは1つですか…。恣意的かつ大胆なデザイン

cargo new してできたフォルダには Cargo.toml があるので、cargo new で作られたものは、まごうことなくパッケージであると理解できた。それでは、クレートはどこにあるのだろうか?

src/main.rs is the crate root of a binary crate with the same name as the package. Likewise, Cargo knows that if the package directory contains src/lib.rs

src/main.rs が、パッケージと同名のバイナリクレートのクレート・ルート。src/lib.rs がパッケージと同名のライブラリクレートのクレート・ルート ⇒ このファイルの存在が、クレートの定義と同義。パッケージと同名のバイナリクレートを作りたかったら src/main.rs を置けば良くて、ライブラリクレートを作りたかったら、src/lib.rs を置くしかない(パッケージに作れるライブラリクレートは最大1個までだから)ということ。ライブラリを作成するにあたっては、パッケージとクレートは一対一対応している。

ところで、先ほど「パッケージ内には好きな数だけバイナリクレートを作ることができる」とあった、それを実現するにはどうすればよいのだろう?

A package can have multiple binary crates by placing files in the src/bin directory: each file will be a separate binary crate.

src/bin ディレクトリを作ってその下に、ファイルを作ればよい。それぞれのファイルが別々のバイナリクレートになる ⇒ さっき src/main.rs はクレート・ルートだと説明を受けたが、src/bin 以下のファイルはクレートになる。それはクレート・ルート?説明一貫してなくないか?

まとめ
  • cargo new では、パッケージと、パッケージと同名のクレートのクレート・ルートが作成される
  • ライブラリクレートを作りたかったら、クレート・ルートとなる src/lib.rs ファイルを置く
  • パッケージと同名のバイナリクレートを作りたかったら、クレート・ルートとなる src/main.rs ファイルを置いて main 関数を書く
  • それ以外のバイナリクレートを作りたかったら src/bin フォルダを置いて、任意の .rs ファイルを置くと、そのファイルひとつひとつが、バイナリクレートのクレート・ルートになる
ここが残念
  • クレートとは、プログラムの構造上は、プログラムの階層構造の中では、モジュールの上に来る名前空間であるのと同時に、Rustのコンパイル単位のことでもある。
    • モジュールの上に来る名前空間はパッケージで良くて、実際一対一対応しているので、冗長と言える
    • Rustのコンパイル単位であるかどうかということは、コンパイラの実装の詳細でプログラマには直接関係ないのでつまびらかにしなくてもよいのでは。多くのRustユーザにはあまり必要ない
  • crates.io で検索して cargo add で依存関係を付けているのはパッケージ(に一対一対応したクレート)。パッケージのことをクレートと呼んで、クレートはRustコンパイラの実装の中に留めていたほうが良かったのではないか
  • Cargo.toml には [workspace] という概念があって、パッケージの中にパッケージが作れる。「パッケージにライブラリクレートは最大1個」しか作れないが、パッケージは作れる。こんな詭弁が許されるのは、愛されているが故なのか。ここはもう少し掘り下げて調べてみることにする予定
  • モジュール化の根幹にある crate がコンパイル単位であり、ライブラリのことであり、実行ファイルでもあり、パッケージも含意している多義的で曖昧な用語になっている。語彙が乏しい人が作った残念なライブラリ臭がする。美しい世界ではない
  • 「クレートはコンパイル単位であり、パッケージはクレートのメタ情報を定義する場所」であることは、少なくとも言えそう。それ以外の説明は The Book を持ってしても的確では無いように思えてならない

続Rust雑感

Rust雑感 - 空想犬猫記を書いてから、おおよそ5年が経とうとしている。Rustはその後も進化を続けて、ついにはLinuxデバイスドライバの開発言語の地位を得たり、Androidの新規開発のデフォルトの言語にもなり、具体的な成果も報告され、ついに外堀が埋まった感がある。言語の印象はここ5年であまり変わらず、あまり好きな部類ではないが。

GCが働く前に終了するような短命のコマンドラインであったり、それほど負荷のかからないバックエンドを書くにあたっては、Go言語で書いてGCを手なずけるほうがずっと効率的である。ただ、ソフトウェア実行効率がコストに直結するクラウド上であったり、同時に計算性能でしのぎを削る分野では、なんといってもRustが、今後10年プログラマの最強の武器になることは、ほぼ決まった。(*JavaC#は、スーツを着たマネージャが若輩プログラマをこき使うための言語のような気がしていて、何となく敬遠している)

というわけで、渋々Rustの再々々々々入門を開始。勉強したことのメモを少しずつ書いていこうと思う。