CASL2 Playgroundの実装ちょっと変えた
以前、WebブラウザでCASL2を動かすCASL2 Playgoundを実装しました。
その時の日記に書いていたのですが、作り変えたい部分があったので作り変えました、という日記です。
変更前
アセンブラとして機械語を生成する処理と、Commet2上で動きをシミュレートするための関数と、両方をごちゃっと実装してしまっている感じがあって分かりづらい。
これは以前のブログ記事にも貼った画像。入力となるソースコードをアセンブルして機械語に変換、同時にCommet2上での動きをシミュレートする関数を生成する。
画面の「Step over」ボタンをクリックする度に1行分シミュレート関数を実行する。
アセンブル時に機械語だけでなく、シミュレート関数も生成してしまっているのがとても分かりづらい。
変更後
Commet2で動かす部分については「言語実装パターン」に書かれている パターン28 レジスタ方式バイトコードインタプリタ を参考しながら機械語を入力とするインタプリタとして作り直すと良さそう。
変更後はこうしました。入力となるソースコードをアセンブルして機械語に変換しメモリに保存するassemberと、メモリから機械語を読み取り処理を実行するinterpreterに分離。interpreterは以前のブログ記事にも書いた通り「言語実装パターン」という書籍のパターン28 レジスタ方式バイトコードインタプリタを参考に実装した。
メモリから2byte (CASL2では1語が2byte)ずつ読み取り、読み取った機械語で分岐する。
// 機械語の命令 const LD = 0x14 const ADDA = 0x24 ... export class Interpreter { PR: GeneralRegister memory: Memory ... constructor( PR: GeneralRegister, memory: Memory, ... ) { this.PR = PR this.memory = memory // アセンブルした結果が格納されてる ... } step(): boolean { const [opcode, operands] = this.readWord() advancePR(this.PR, 1) // 参照するメモリ1語分だけ進める switch (opcode) { case LD: this.ld(operands) break case ADDA: this.adda(operands) break ... } private readWord(): [number, number] { const word = this.memory.lookup(this.PR.lookup()) const upperByte = word >> 8 const lowerByte = word & 0b11111111 return [upperByte, lowerByte] } }
JavaScriptでバイト列を扱いたい時は、ArrayBufferで領域を確保、読み書きはTypedArrayかDataViewでArrayBufferをwrapしてからやる。CASL2では2byteのうち、先頭1byteが命令、それ以降がoperandという仕様であり、ビッグエンディアンで読み書きしたい。エンディアンを明示的に指定する場合はDataViewを使う。
元の実装と比べて実際の挙動に近くなったと思うし、コードも読みやすくなった!
ちなみに「言語実装パターン」というのはこの書籍です。
おしまい
書き換えたいなぁとモヤモヤが残っていたのですっきり!!
AndroidでgRPCクライアント
AndroidでgRPCクライアントの実装をしようと思ってチュートリアル読んだけど、すっと分からなかったのでメモ。まずは、Goで簡単なgRPCサーバーとクライアントを実装、その後クライアントをAndrodアプリとして実装する、というのをやってみました。
実装してみたソースコードはここです↓
gRPCがどんなものなのか触る
まず、Goで実装してみる。以下の記事が進めやすかったです、ありがとうございます。
この記事の通りに進める。
- protoファイルでクライアント・サーバー間のインタフェースを定義する
- protocコマンドでprotoファイルからクライアントとサーバーの通信部分についてコード自動生成する
- 自動生成したコードを利用するサーバーとクライアントを実装し動かして雰囲気を掴む
クライアントをAndroidアプリで実装する
上記のGoで実装したクライアントはCLIを持つプログラム。このクライアントをAndroidアプリにしてみる。
gRPC公式のQuick start
まずはgRPCの公式ドキュメントのAndroidのページを見に行く。
Quick start | Kotlin for Android | gRPC
以下のプロジェクトを使って説明されている。確認したときの最新のtagは v1.3.0
。
grpc-kotlin/examples at v1.3.0 · grpc/grpc-kotlin
このプロジェクトはgradleで構成されている。色々あってどこを見ればいいかすぐに分からなかったけど、AndroidアプリでgRPCクライアントについては3つのディレクトリ(Gradleのモジュール)だけを見れば良い。
examples/protos
はprotoファイルが置いてあるモジュール。 examples/android
はgRPCクライアントとなるAndroidアプリのモジュール。
では example/stub-android
は?
stub
example/stub-android
は、protoファイルからクライアントコードを自動生成するためのモジュール。生成されたクライアントコードをstubと呼ぶ。クライアントとなるAndroidアプリのソースコードからstubのメソッドを普通のメソッドとして呼び出すが、stubが提供しているメソッドの実態はサーバー側で実装されている。つまり、RPCとなる。
ユニットテストで仮の実装としてスタブという言葉を使うことがあるけど、仮の実装というわけではない。
stubについて、gRPC公式で軽く触れられている。
Androidアプリつくる
上記のexampleを参考に作ってみた。
project_root/app
がAndroidアプリ、 feature/grpc-client
サブモジュールがgRPCクライアントのソースコードで、公式exampleの android
に相当する。
このAndroidアプリは client_stub
モジュールに依存する。他にもgRPC関連のライブラリにも依存する。
dependencies { implementation(project(path: ":client_stub")) ... // gRPC implementation("io.grpc:grpc-kotlin-stub:$grpc_kotlin_version") implementation("com.google.protobuf:protobuf-kotlin-lite:$protobuf_version") implementation("io.grpc:grpc-android:$grpc_android_version") implementation("io.grpc:grpc-okhttp:$grpc_android_version") }
feature/grpc-client/build.gradle
client_stub
はprotoファイルからコード自動生成するモジュール。Android Libraryモジュールとする。
公式exampleの build.gradle
を参考に色々いじってみる。
protobuf { protoc { artifact = "com.google.protobuf:protoc:$protobuf_version" } plugins { java { artifact = "io.grpc:protoc-gen-grpc-java:$grpc_version" } grpc { artifact = "io.grpc:protoc-gen-grpc-java:$grpc_version" } grpckt { artifact = "io.grpc:protoc-gen-grpc-kotlin:$grpc_kotlin_version:jdk7@jar" } } ... }
以下の3つそれぞれとprotocコマンドとの対応をイメージすると把握しやすい。Java実装のstubを自動生成しないといけないというのが最初とっつきづらかった。
com.google.protobuf:protoc
protoc
コマンド自体
io.grpc:protoc-gen-grpc-java
- Javaで実装されたstubを生成
- Goの例で言うと
protoc
コマンドのオプション--go-grpc_out
や--go-grpc_opt
io.grpc:protoc-gen-grpc-kotlin
- Kotlinで実装された上記のJava実装stubのラッパー
protocから自動生成したstubをAndroid Libraryモジュールとして、 project_root/app
から参照できれば、あとは公式exampleのandroidを見ながら実装すれば良い。
おしまい
protocコマンドで生成できるととっつきやすいのですが、Android Libraryモジュールにしたい場合はGradleでやれると楽ですねぇ。今回は client_stub
を同一プロジェクトにしましたが、別リポジトリとしてaarをpublishするのが良さそうです。
確率木って便利ですよね・・・?
この記事は統計学 Advent Calendar 2022 24日目の記事です!
確率木というのを Head First Statistics という本で知りました。
条件付き確率とベイズの定理について確率木を使うとすごく分かりやすいなぁと思ったのですが、この本以外では見かけたことがなく何か良くない点もあるのでしょうか・・・?もしあるなら知りたい・・・!
確率木を使ってみる
例として、統計Webの問題をやってみます。
3つの袋があり、次のように赤い玉と白い玉が入っています。
- 袋1:赤い玉4つ、白い玉1つ
- 袋2:赤い玉3つ、白い玉3つ
- 袋3:赤い玉2つ、白い玉4つ
いずれかの袋から玉を1つ取り出したところ、白い玉でした。この玉が袋2から取り出された確率はいくらでしょうか。
まず3つの袋のどれかを選びます。次にそれぞれの袋について赤or白を選びます。これを以下のような木で表現します。
確率木の枝に、その枝が選ばれる確率を書きます。袋はどれを選ぶ確率も同じ です。
枝の確率をすべて足すと になりますね。
確率木は1つの節から生えている枝の確率をすべて足すと になります。例えば、袋1から生えている各枝の確率をすべて足すと です。
袋1には、赤い玉4つ、白い玉1つ、全部で5つ入ってます。袋1から玉を1つ取り出すとき、赤い玉の確率は 、白い玉の確率は です。袋2, 3についても同じように確率が分かるので、それぞれ確率木に書き込みます。
根から袋1の枝の確率は 、袋1から白の枝の確率は です。葉の位置する「袋1かつ白」となる確率 は葉に至るルートの確率をすべて掛け算すればいいことになります。
白玉を選ぶ場合の確率をすべて書き込みます。
知りたいことは、 いずれかの袋から玉を1つ取り出したところ、白い玉でした。この玉が袋2から取り出された確率はいくらでしょうか。
なので以下の条件付き確率を求めます。
分子の値はすでに分かっています。分母の 、つまり白玉を取り出す確率は、確率木を見ると白玉を選ぶ葉の確率を全部足せば良いのですぐに分かります。
これで求めることができました。
おしまい
最後の式はベイズの定理 に当てはめた形になっているわけですが、公式はあんまり覚えてないし、ベン図での定理の説明も分かった気はしているのだけどスッと解けないみたいな状態でした。
確率木に出会ったことで直感的に腑に落ちたように感じることができて、とても良かったです!