Compare commits
27 Commits
0eba0d32f6
...
v4
Author | SHA1 | Date | |
---|---|---|---|
f1f31100c5 | |||
77dc3f577a | |||
5f422bf2ec | |||
63a356bc95 | |||
49594d698b | |||
33afe3ca27 | |||
665489d7a7 | |||
90b0e36115 | |||
685180aa47 | |||
8f1fd8c048 | |||
ac6c358150 | |||
bda19719a9 | |||
05a154c745 | |||
de3da1f6a1 | |||
3d7c9c9048 | |||
b32ef1d348 | |||
bb453aba6b | |||
bab3afd790 | |||
17df20a502 | |||
51fa440e02 | |||
245acb784e | |||
baa447e054 | |||
b595629042 | |||
08a1cc5b30 | |||
81fbd200a8 | |||
6ef107b199 | |||
b3646b2c71 |
23
content/Entity Component System.md
Normal file
23
content/Entity Component System.md
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
#programming
|
||||||
|
|
||||||
|
|
||||||
|
主にゲームエンジンなどで採用されるプログラミングのパラダイム。
|
||||||
|
|
||||||
|
最近のUnityでも内部的に採用されている。
|
||||||
|
|
||||||
|
[[Rust]]だと[[Bevy]]が有名。
|
||||||
|
|
||||||
|
[[オブジェクト指向]]と比べると、データのメモリ分布が、オブジェクトごとに並ぶのではなく個別のメンバー変数ごとに並ぶことになり、CPUのメモリキャッシュに乗りやすいなどの利点がある。
|
||||||
|
|
||||||
|
[\[Rust\] ECSアーキテクチャ \[bevy\_ecs\] | DevelopersIO](https://dev.classmethod.jp/articles/ecs-rust-bevy/)
|
||||||
|
|
||||||
|
[Intro to ECS - Unofficial Bevy Cheat Book](https://bevy-cheatbook.github.io/programming/ecs-intro.html)
|
||||||
|
|
||||||
|
## データのモデリング方法としてなにがうれしいのか
|
||||||
|
|
||||||
|
オブジェクト指向と比べた時の利点が基本的にさっきのような、パフォーマンス面での利点が強調されることが多い。ただ、何かパフォーマンスのためにせっかくプログラミング言語が用意してくれたデータのモデリング技法を犠牲にしているような気がしてならず、あんまり旨味がよくわからなかった。
|
||||||
|
|
||||||
|
ただ、Bevyの設計をいろいろ読んでいると、モデリングとしての核心は、「集合の中から特定の要素を持つもの部分集合を指定して挙動を個別に操作する(依存性がない操作同士はどういう順番で実行してもいい)」ということかなと思った。
|
||||||
|
|
||||||
|
|
||||||
|
|
9
content/Hackett.md
Normal file
9
content/Hackett.md
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
#programming-language
|
||||||
|
|
||||||
|
[[Racket]]上で実装された、静的型付けなうえで、型安全なマクロシステムを搭載したプログラミング言語
|
||||||
|
|
||||||
|
[1 The Hackett Guide](https://lexi-lambda.github.io/hackett/guide.html)
|
||||||
|
|
||||||
|
["Hackett: a metaprogrammable Haskell" by Alexis King - YouTube](https://www.youtube.com/watch?v=5QQdI3P7MdY)
|
||||||
|
|
||||||
|
|
37
content/Red Green Syntax Tree.md
Normal file
37
content/Red Green Syntax Tree.md
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
#compiler-design
|
||||||
|
|
||||||
|
[Red-Green Trees: an Overview. Six months ago I dug into Roslyn’s… | by Bayastan | Jun, 2025 | Medium](https://medium.com/@krendelia2021/red-green-trees-an-overview-17bae2d84e8c)
|
||||||
|
|
||||||
|
[[抽象構文木]]というか[[具象構文木]]の実装方法の一つ。
|
||||||
|
|
||||||
|
構文の途中で差し込まれたコメントなどのトリビアなどを保持して、完全なソースコードに戻せるけど実行効率などをよくしたままにできるやり方。
|
||||||
|
|
||||||
|
部分的に構文木を書き換えたりするとソースコードのロケーション情報がずれたりするので、文字幅とオフセットを別々に持たせるというやり方をしている
|
||||||
|
|
||||||
|
ながいけどこれがわかりやすい
|
||||||
|
|
||||||
|
[Ruby Parser開発日誌 (19) - 最高の構文木の設計 2024年版 - かねこにっき](https://yui-knk.hatenablog.com/entry/2024/08/23/113543)
|
||||||
|
|
||||||
|
[[C#]]のRoslynで提案されたもので、[[rust-analyzer]]とかでも使われている
|
||||||
|
|
||||||
|
- Green Treeが構文的に意味のある単位でのツリーで、Childrenを持ち、Spanの絶対位置ではなく幅を持つ
|
||||||
|
- Red Treeは必要に応じて構築される木で、Green Nodeへの参照と親への参照、ドキュメントトップからのSpanの先頭位置を持つ
|
||||||
|
|
||||||
|
これで、SpanはRed-GreenTreeの組み合わせでアドホックに生成できるから、部分的な木の差し替えとかにも対応しやすくてうれしい
|
||||||
|
|
||||||
|
## これってRustでもうちょっとシンプルに作れんのか
|
||||||
|
|
||||||
|
|
||||||
|
[[Rust]]で普通にSyntax Tree作ってると、Recursiveなenumを経由して作るのが普通なわけだが、適当なid-arena的仕組みで管理している限りは、普通のSyntax TreeがGreen Tree
|
||||||
|
|
||||||
|
|
||||||
|
愚直に参考にされているやつを使うと、Tagged Unionをゼロから再実装するみたいなことになるので、Rustのenum as Tagged Unionの構造を有効活用したくね?
|
||||||
|
|
||||||
|
|
||||||
|
Rust Analyzerだと文字列だけじゃなくSyntax TreeまでInterningされてるらしい(Rust規模のコードベースなら確かに有効かも)
|
||||||
|
|
||||||
|
[rust-analyzer/docs/dev/syntax.md at c9109f23de57359df39db6fa36b5ca4c64b671e1 · rust-lang/rust-analyzer · GitHub](https://github.com/rust-lang/rust-analyzer/blob/c9109f23de57359df39db6fa36b5ca4c64b671e1/docs/dev/syntax.md)
|
||||||
|
|
||||||
|
というかrowanそのままつかえばいいのかな
|
||||||
|
|
||||||
|
[GitHub - rust-analyzer/rowan](https://github.com/rust-analyzer/rowan)
|
13
content/ecsact.md
Normal file
13
content/ecsact.md
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
#programming-language
|
||||||
|
|
||||||
|
[Ecsact](https://ecsact.dev/)
|
||||||
|
|
||||||
|
[[Entity Component System]]を言語のコアに組み込んだプログラミング言語とランタイムシステム。
|
||||||
|
|
||||||
|
Componentの仕様と、システムの型宣言のところまではシンプルな言語として実装されていて、宣言されたSystemの実装をどうするかはLanguage-Agnosticな形に任せるという方式を取っている
|
||||||
|
|
||||||
|
ランタイムのAPIが言語としても公開されているので、これを経由してSystemを実装する。
|
||||||
|
ゲームエンジンへの埋め込みを想定しているっぽい
|
||||||
|
|
||||||
|
システムを統一した言語で書けるわけではないのがいいのか悪いのかわからんけど、宣言部だけなら[[ProtoBuf]]的な役割を担えそうな予感はする
|
||||||
|
|
@@ -30,6 +30,7 @@ https://github.com/mimium-org/mimium-rs
|
|||||||
- [[mimiumのMIRコンパイル過程を真面目に考える]]
|
- [[mimiumのMIRコンパイル過程を真面目に考える]]
|
||||||
- [[lambda-mmm(実用版)]]
|
- [[lambda-mmm(実用版)]]
|
||||||
- [[mimiumグローバル環境評価について]]
|
- [[mimiumグローバル環境評価について]]
|
||||||
|
- [[mimiumのモジュールシステム]]
|
||||||
|
|
||||||
|
|
||||||
## マクロ
|
## マクロ
|
||||||
@@ -51,6 +52,13 @@ https://github.com/mimium-org/mimium-rs
|
|||||||
|
|
||||||
ふと思ったけど、[[SuperCollider]]や[[PureData]]と比べると、これらの言語は組み込みに使おうと思うとLinuxが動く環境を想定することになる([[Heavy]]はそれを全く別の処理系作ることで対応してたけど)。シーケンサとかスケジューラーがあるような、[[Faust]]だと難しいタイプのプログラムを[[Arduino]]とかに持っていくには向いているのではないか([[Extempore]]だって仕組み的に言えばそうかもしれないけど)
|
ふと思ったけど、[[SuperCollider]]や[[PureData]]と比べると、これらの言語は組み込みに使おうと思うとLinuxが動く環境を想定することになる([[Heavy]]はそれを全く別の処理系作ることで対応してたけど)。シーケンサとかスケジューラーがあるような、[[Faust]]だと難しいタイプのプログラムを[[Arduino]]とかに持っていくには向いているのではないか([[Extempore]]だって仕組み的に言えばそうかもしれないけど)
|
||||||
|
|
||||||
|
- できれば教育用途とかに持ち込めるのが一番いい
|
||||||
|
- ブートストラップできるといい
|
||||||
|
- いろいろな人がmimiumで拡張やライブラリを書けるようになってからが本当の本番
|
||||||
|
- 言語自体の拡張機能をその言語上でたくさん作れるとよい
|
||||||
|
- しかし、極端に自由度が高いとそれはそれで参入障壁が高い
|
||||||
|
|
||||||
|
|
||||||
[[mimiumでのシーケンサ]]
|
[[mimiumでのシーケンサ]]
|
||||||
|
|
||||||
[[mimiumでのライブコーディングエンジン]]
|
[[mimiumでのライブコーディングエンジン]]
|
||||||
|
@@ -68,4 +68,59 @@ Param型はinvokeでuiからの値を取れる、サンクをアンラップす
|
|||||||
|
|
||||||
結局、読み込んで返す値として次元数の値を持つ配列を返すしかないのかも
|
結局、読み込んで返す値として次元数の値を持つ配列を返すしかないのかも
|
||||||
|
|
||||||
|
ただ、よく考えると[[Max]]のsfplayとかだって、ファイルのチャンネル数とは別に出力のチャンネル数先に指定するしな(入ってるファイルの中身のCh数に応じて処理を分岐させるというユースケースがあんまり思いつかない)
|
||||||
|
|
||||||
|
## シンタックス
|
||||||
|
|
||||||
|
```rust
|
||||||
|
let x = `{1+2} //コード型のシンタックス
|
||||||
|
${x} //エスケープ/スプライスのシンタックス
|
||||||
|
//or
|
||||||
|
$x
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
### シンタックスシュガー
|
||||||
|
|
||||||
|
ファイル内でステージを変えたブロックを作りたい場合、ブロックがどんどんネストされていって面倒なので
|
||||||
|
|
||||||
|
```rust
|
||||||
|
//ステージ1からスタート
|
||||||
|
|
||||||
|
stage(-1){
|
||||||
|
//ここはステージ0
|
||||||
|
let x = {...}
|
||||||
|
}
|
||||||
|
//一度ステージ1へ
|
||||||
|
|
||||||
|
stage(-1){
|
||||||
|
//ここはステージ0
|
||||||
|
let y = (x+...)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
これをこういう形でネストした式へ変形
|
||||||
|
```rust
|
||||||
|
//コンパイラはステージ0からスタートするためにCodeを追加
|
||||||
|
`{
|
||||||
|
//ステージ1からスタート
|
||||||
|
${
|
||||||
|
//ステージ0
|
||||||
|
let x = {...}
|
||||||
|
`{
|
||||||
|
//一度ステージ1へ
|
||||||
|
}
|
||||||
|
${
|
||||||
|
//再度ステージ0へ
|
||||||
|
let y = (x+...)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
この、最後に閉じ括弧が溜まっていく形にしない限り、yはxを参照できなくなってしまう
|
||||||
|
|
||||||
|
|
||||||
|
ステージ表記は相対表記がいいのかしら
|
||||||
|
|
||||||
|
180
content/mimiumのモジュールシステム.md
Normal file
180
content/mimiumのモジュールシステム.md
Normal file
@@ -0,0 +1,180 @@
|
|||||||
|
#mimium
|
||||||
|
|
||||||
|
モジュールシステムの意味論と実装について考える
|
||||||
|
|
||||||
|
### 必要要件
|
||||||
|
|
||||||
|
理想としては、以下を全部満たしたい
|
||||||
|
|
||||||
|
- 分割コンパイルしたうえで、コンパイル済みのモジュールは変更がなければ再利用できる
|
||||||
|
- 操作的意味論的にもある程度一貫性がある
|
||||||
|
- モジュール内ではシンボルの相互参照が可能(後から定義されるシンボルでも参照可能)
|
||||||
|
|
||||||
|
で、デザインチョイスのトレードオフとしては意味論を楽にしようと思うと分割コンパイルがめんどくさくなる
|
||||||
|
|
||||||
|
複数のコードを合体させるときに、流用されるコードの複製が増える問題
|
||||||
|
|
||||||
|
ただ、どっちみちDSPにおけるコードの量なんてたかが知れているのでは?という問題も
|
||||||
|
|
||||||
|
実際、そこまでモジュール内での相互参照って必要か?というのもある、Letrecで十分じゃね?
|
||||||
|
|
||||||
|
モジュールの循環参照が起きたらどうなるのかという問題もある
|
||||||
|
|
||||||
|
|
||||||
|
#### モジュール内での宣言一覧
|
||||||
|
```rust
|
||||||
|
//関数のvisibilityセッティングはRustとおなじ感じ
|
||||||
|
pub fn (){
|
||||||
|
|
||||||
|
}
|
||||||
|
pub const Foo = 100
|
||||||
|
pub type Bar = Constructor(()->float) //新しい型宣言
|
||||||
|
pub type alias Hoge = ()->float //エイリアス宣言
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
#### シンタックス:モジュールの宣言
|
||||||
|
|
||||||
|
```rust
|
||||||
|
mod modname{//ソースファイルと同じ宣言
|
||||||
|
//toplevel_decls
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```rust
|
||||||
|
mod(stage-1){//ステージの変更
|
||||||
|
//
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- モジュール単位でMIRとバイトコードを生成して、あとからリンクできるようにする
|
||||||
|
- ただし、ステージ0マクロの展開もしないといけないので、ASTも出力して保持していないといけない
|
||||||
|
|
||||||
|
モジュールを値としてレコード型に型付けできると話が早いんだけど、それ一級モジュールの機能だよな
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#### 意味論
|
||||||
|
|
||||||
|
```rust
|
||||||
|
stage(1)
|
||||||
|
mod(0) modname{
|
||||||
|
//toplevel_decls
|
||||||
|
fn foo()-> `float {
|
||||||
|
...
|
||||||
|
}
|
||||||
|
}
|
||||||
|
modname::foo!()
|
||||||
|
```
|
||||||
|
これがあったとすると、
|
||||||
|
|
||||||
|
```rust
|
||||||
|
extern modname = ${
|
||||||
|
foo = | | { },
|
||||||
|
...
|
||||||
|
}
|
||||||
|
|
||||||
|
modname.foo!()
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
こういう感じかなー、あ、でもこうするとモジュール内での相互参照が解決できないか
|
||||||
|
|
||||||
|
トップレベルでのモジュールの名前解決は後から定義されたシンボルも参照できるので、型推論のやり方はなんか考える必要がありそう
|
||||||
|
|
||||||
|
一旦トップレベルの宣言の名前だけを回収して、あとから実際の定義の型推論を行っていく形になる
|
||||||
|
|
||||||
|
しかし、そうするとラムダ計算として定義する旨味はあんまりないのでは、という気がしてくる
|
||||||
|
|
||||||
|
`extern name : Type `という宣言に変換さえできれば意味論を保ってコンパイルはできそう
|
||||||
|
|
||||||
|
型がつけられさえすればいいから、そこで循環する定義にならなければOK
|
||||||
|
|
||||||
|
```rust
|
||||||
|
//modA.mmm
|
||||||
|
use modB
|
||||||
|
pub fn hoge(){
|
||||||
|
modB::hoge()
|
||||||
|
}
|
||||||
|
|
||||||
|
//modB.mmm
|
||||||
|
use modA
|
||||||
|
pub fn hoge(){
|
||||||
|
modA::hoge()
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
(ランタイムで無限ループするけどそれはいいとして)変換するとこう
|
||||||
|
|
||||||
|
```rust
|
||||||
|
// modA.mmm
|
||||||
|
extern modB:{
|
||||||
|
hoge: ?
|
||||||
|
}
|
||||||
|
fn hoge(){
|
||||||
|
modB.hoge()
|
||||||
|
}
|
||||||
|
|
||||||
|
//modB.mmm
|
||||||
|
extern modA:{//実際は型コンストラクタとかで覆って区別できるようにすべき
|
||||||
|
hoge: ?
|
||||||
|
}
|
||||||
|
fn hoge(){
|
||||||
|
modA.hoge()
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
`modA::hoge : 'a ()->'a, modB::hoge : 'a ()->'a `
|
||||||
|
|
||||||
|
ここまでしか型は決定できないということに
|
||||||
|
|
||||||
|
ただまあ、型が決定不能ならその時コンパイルエラーにすればいいだけで、一応型推論自体は無限ループに陥らず完了するのか?ただ、extern宣言に変えるためには一回すべてのモジュールを読み込んでみてからでないと名前一覧すら作れない
|
||||||
|
|
||||||
|
Rustの場合はグローバルな宣言が基本的に型推論しなくて済むようになっているからなあ
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
型付け手順
|
||||||
|
|
||||||
|
- モジュールA読み込み開始
|
||||||
|
|
||||||
|
- いったんuse宣言以外の全部のProgramStatementを読んで名前をグローバル空間に登録(型宣言は関数などわかるところ以外はすべて変数として登録)
|
||||||
|
- モジュールー型宣言マップにファイル名登録
|
||||||
|
- use modBの解釈開始
|
||||||
|
- マップに問い合わせ、ファイルがないのでファイル読み込み開始
|
||||||
|
- use modAの解釈開始
|
||||||
|
- マップに問い合わせ、ファイルがあったのでそれを参照
|
||||||
|
- とりあえずmodAの型はすべて不明なレコード型として解釈して型付け開始
|
||||||
|
- Proigram→Exprへの変換の中でuse宣言をextern modA:{...}に変換し、名前空間参照をレコードへのアクセスに変換
|
||||||
|
|
||||||
|
両方が推論終わってから再度Unificationしないといけないか?これも再帰を禁止しておけばそれでいいのか
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
```
|
||||||
|
Program = FunctionDefinition
|
||||||
|
|GlobalDeclaration
|
||||||
|
|ModuleDeclaration
|
||||||
|
|use ModuleName
|
||||||
|
ModuleDeclaration = Visibility(stage) { Program }
|
||||||
|
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
Pythonのモジュールシステム [Python's Import System - Module object|Regular/Namespace Packages|Finders & Loaders|Relative imports - YouTube](https://www.youtube.com/watch?v=QCSz0j8tGmI)
|
||||||
|
|
||||||
|
モジュールはオブジェクトであり、`__file__`などはモジュールオブジェクトに付属するメンバ変数
|
||||||
|
|
||||||
|
|
||||||
|
[[Gluon]]のモジュール [Modules - Gluon Documentation](https://gluon-lang.org/doc/crates_io/book/modules.html)
|
||||||
|
|
||||||
|
これマクロとして実装してあって、それこそレコードとして出力されるだけなので、分割コンパイルとかモジュール内相互参照とかは全然考慮されてないけど意味論はすっきり
|
||||||
|
|
||||||
|
|
@@ -147,7 +147,7 @@ myugen({ default_v <- freq = 200.0 })
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 部分型と型クラス
|
## 部分型と[[型クラス]]
|
||||||
|
|
||||||
[[構造的部分型]]を採用するつもり。
|
[[構造的部分型]]を採用するつもり。
|
||||||
|
|
||||||
|
@@ -123,6 +123,32 @@ fn reverse(origin:Region)->Region{
|
|||||||
- 再生前(prepareToPlay)
|
- 再生前(prepareToPlay)
|
||||||
- 信号再生時(process)
|
- 信号再生時(process)
|
||||||
|
|
||||||
|
## 多段階計算と組み合わせる
|
||||||
|
|
||||||
|
[[mimiumと多段階計算]]で、それなりに多段階計算の実装が間に合ってきた。
|
||||||
|
|
||||||
|
FadeinOutのようなリージョン→リージョンの関数はステージ0の計算と考えることができる。
|
||||||
|
|
||||||
|
また、Generator系も、基本的には周波数や音量といったUIパラメーターは、ステージ0での評価時にUIを生成して値を受け取るチャンネルを作り、ステージ1=再生中にそのUIからの値を受け取るという方式で捉えられる
|
||||||
|
|
||||||
|
```rust
|
||||||
|
fn audiofx(param1=100,param2=200){
|
||||||
|
`|input|{... }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn gen_component(){
|
||||||
|
Param{..} |> sinosc
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
`Param`はジェネリックな関数とする
|
||||||
|
|
||||||
|
まあこれつまり、Temporal Type Constuctorだけだとパラメーター周りのIOのライブ入出力について十分に考慮されていないということになるのかな
|
||||||
|
|
||||||
|
フェードイン/アウトを掛けた状態のものを、複製とかも含めて考えるとどうなるんだろ
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
---
|
---
|
||||||
以下は昔に考えていたこと
|
以下は昔に考えていたこと
|
||||||
|
|
||||||
|
Submodule content/private updated: 8d468015c6...421f5c248c
10
content/コンパイラを書く時の悩みについて.md
Normal file
10
content/コンパイラを書く時の悩みについて.md
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
#compiler-design
|
||||||
|
|
||||||
|
- ある程度コンパイラのコードベースが大きくなってくると、認知負荷がでかくなってくる
|
||||||
|
- かといって、コードをコンパクトに保とうとすると、一箇所の変更がしづらくなっていく
|
||||||
|
- かといって、頑張ってインターフェースを切って分離性を高めていくと、全体の把握はしづらくなる
|
||||||
|
- そりゃ全体を把握しなくいても継続的に開発できるようにするのが目的なのでそうなんだけど
|
||||||
|
- 例えば、シンタックスツリーに[[Red Green Syntax Tree]]をあとから使おうとすると、さすがに書き換えが大変
|
||||||
|
- だが、Language Serverを作ろうと思ったらいつかは必要
|
||||||
|
- これをインクリメンタルに開発するのは無理だよなあ
|
||||||
|
-
|
41
content/コードフォーマッター.md
Normal file
41
content/コードフォーマッター.md
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
---
|
||||||
|
date: 2025-07-22 17:00
|
||||||
|
---
|
||||||
|
#programming-language
|
||||||
|
|
||||||
|
一般的なコードフォーマッターの実装について。
|
||||||
|
|
||||||
|
[How to write a code formatter](https://yorickpeterse.com/articles/how-to-write-a-code-formatter/)
|
||||||
|
|
||||||
|
1行当たり最大文字数がこのくらい、としたときに、どういう戦略で折り返すかは結構難しい問題。
|
||||||
|
|
||||||
|
文字列→トークン→ASTという順番で変換されるので、この逆順が良いのかとも思ったが、
|
||||||
|
|
||||||
|
AST→フォーマッタ用の専用の木構造みたいな中間表現を一度挟んだほうが賢いのかもしれない
|
||||||
|
|
||||||
|
[[Rust]]の[pretty](https://docs.rs/pretty/latest/pretty/)クレートがコンビネーターとして定義してあるやつ
|
||||||
|
|
||||||
|
[[Tree-sitter]]を使った汎用フォーマッター[Topiary](https://topiary.tweag.io/)とかいうのもある
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
Exprの途中に差し込まれたトリビア(主にコメント)をどうやって抽出するか
|
||||||
|
|
||||||
|
ExprNodeIdに対するSecondary MapがSpanに対して作れているのだから、Trailng Triviaとしてコメントを保持するのは一応できるか?
|
||||||
|
|
||||||
|
パーサコンビネータでどうにか処理できるもんか?
|
||||||
|
|
||||||
|
```rust
|
||||||
|
fn parse_expr_top<Output>()->impl Parser<Token,Output,Error>{
|
||||||
|
not(comment()).padded_by(comment().repeated())
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
[Parser in chumsky - Rust](https://docs.rs/chumsky/latest/chumsky/trait.Parser.html#method.map_with)
|
||||||
|
|
||||||
|
`map_with`使えばいけるかしら
|
||||||
|
|
||||||
|
Stateにトリビアを書き込んでおけばいいのか
|
||||||
|
|
||||||
|
|
15
content/型クラス.md
Normal file
15
content/型クラス.md
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
#programming-language
|
||||||
|
|
||||||
|
[[Rust]]におけるTraitとかに近いもの
|
||||||
|
|
||||||
|
あるメソッド群を持つジェネリックな型の分類
|
||||||
|
|
||||||
|
パラメトリックなジェネリクスに対して、対象が広すぎるものを、アドホック多相的に制限する
|
||||||
|
|
||||||
|
["Hackett: a metaprogrammable Haskell" by Alexis King - YouTube](https://youtu.be/5QQdI3P7MdY)
|
||||||
|
|
||||||
|
この動画の説明わかりやすかった(12:10~)
|
||||||
|
|
||||||
|
式をもとに型を生成するのが[[型推論]]、型情報をもとに式を生成するのがジェネリクスというループ
|
||||||
|
|
||||||
|
[[Hackett]]
|
122
content/小数から近い分数を求める.md
Normal file
122
content/小数から近い分数を求める.md
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
---
|
||||||
|
date: 2025-07-24 17:54
|
||||||
|
---
|
||||||
|
#memo #mathematics
|
||||||
|
|
||||||
|
例えば、[[双方向プログラミング]]的なもので、数値をスライダーで調整できるようになっていた場合、実際のところは5/7とかわかりやすい有理数だったりとか、整数に近い値を表したいのにめちゃくちゃ長い小数点の数値が残ったりする。
|
||||||
|
|
||||||
|
これを、適当なグリッドにスナップするUIの一つとして、許容できる誤差範囲と整数の複雑さを指定して分数に変換するのが良いのではないか。
|
||||||
|
|
||||||
|
ChatGPTに聞いた。[[連分数展開]]というのを使うと良いらしい。[[Rust]]のコードを書いてもらった。
|
||||||
|
|
||||||
|
```rust
|
||||||
|
/// 任意の実数 x を近似する収束分数・半収束分数を求める
|
||||||
|
/// x: 近似したい実数
|
||||||
|
/// eps: 絶対誤差許容値
|
||||||
|
/// max_den: 分母の上限
|
||||||
|
/// max_num: 分子の上限
|
||||||
|
fn rational_approx(
|
||||||
|
x: f64,
|
||||||
|
eps: f64,
|
||||||
|
max_den: u64,
|
||||||
|
max_num: u64,
|
||||||
|
) -> Vec<(u64, u64)> {
|
||||||
|
// 連分数展開の a_k リストを構築
|
||||||
|
let mut a: Vec<u64> = Vec::new();
|
||||||
|
let mut r = x;
|
||||||
|
for _ in 0..64 {
|
||||||
|
let ak = r.floor() as u64;
|
||||||
|
a.push(ak);
|
||||||
|
let frac = r - (ak as f64);
|
||||||
|
if frac.abs() < f64::EPSILON {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
r = 1.0 / frac;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut candidates: Vec<(u64, u64)> = Vec::with_capacity(a.len());
|
||||||
|
let (mut p_nm2, mut q_nm2) = (0u128, 1u128);
|
||||||
|
let (mut p_nm1, mut q_nm1) = (1u128, 0u128);
|
||||||
|
|
||||||
|
for &ak in &a {
|
||||||
|
// 収束分数
|
||||||
|
let p = ak as u128 * p_nm1 + p_nm2;
|
||||||
|
let q = ak as u128 * q_nm1 + q_nm2;
|
||||||
|
if q > max_den as u128 { break; }
|
||||||
|
let num = p as u64;
|
||||||
|
let den = q as u64;
|
||||||
|
if num <= max_num {
|
||||||
|
let approx = (num as f64) / (den as f64);
|
||||||
|
if (x - approx).abs() <= eps {
|
||||||
|
candidates.push((num, den));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 半収束分数
|
||||||
|
for d in 1..=(ak / 2) {
|
||||||
|
let ap = ak - d;
|
||||||
|
let p2 = ap as u128 * p_nm1 + p_nm2;
|
||||||
|
let q2 = ap as u128 * q_nm1 + q_nm2;
|
||||||
|
if q2 <= max_den as u128 && p2 <= max_num as u128 {
|
||||||
|
let num2 = p2 as u64;
|
||||||
|
let den2 = q2 as u64;
|
||||||
|
let approx2 = (num2 as f64) / (den2 as f64);
|
||||||
|
if (x - approx2).abs() <= eps {
|
||||||
|
candidates.push((num2, den2));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
p_nm2 = p_nm1;
|
||||||
|
q_nm2 = q_nm1;
|
||||||
|
p_nm1 = p;
|
||||||
|
q_nm1 = q;
|
||||||
|
}
|
||||||
|
|
||||||
|
candidates.sort_by(|&(p1, q1), &(p2, q2)| {
|
||||||
|
let e1 = (x - (p1 as f64)/(q1 as f64)).abs();
|
||||||
|
let e2 = (x - (p2 as f64)/(q2 as f64)).abs();
|
||||||
|
e1.partial_cmp(&e2).unwrap()
|
||||||
|
});
|
||||||
|
candidates.dedup();
|
||||||
|
candidates
|
||||||
|
}
|
||||||
|
|
||||||
|
/// x を percent% 精度で近似するラッパー
|
||||||
|
/// x: 近似したい実数
|
||||||
|
/// percent: 相対誤差許容値(%)
|
||||||
|
/// max_num: 分母、分子の上限
|
||||||
|
fn rational_approx_pct(
|
||||||
|
x: f64,
|
||||||
|
percent: f64,
|
||||||
|
max_num: u64,
|
||||||
|
) -> Vec<(u64, u64)> {
|
||||||
|
let eps = (percent / 100.0) * x.abs();
|
||||||
|
rational_approx(x, eps, max_num, max_num)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let x = 2.7182818284;
|
||||||
|
let percent = 1.0; // 1%
|
||||||
|
let max_num = 500;
|
||||||
|
let results = rational_approx_pct(x, percent, max_num);
|
||||||
|
println!("近似候補(percent={}%):", percent);
|
||||||
|
for (p, q) in results {
|
||||||
|
let approx = p as f64 / q as f64;
|
||||||
|
let err = (x - approx).abs();
|
||||||
|
println!("{}/{} = {:.8}, 誤差 {} ({:.4}%差)",
|
||||||
|
p, q, approx, err, err / x.abs() * 100.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
近似候補(percent=1%):
|
||||||
|
193/71 = 2.71830986, 誤差 0.00002803075492963103 (0.0010%差)
|
||||||
|
106/39 = 2.71794872, 誤差 0.00033311045128181505 (0.0123%差)
|
||||||
|
87/32 = 2.71875000, 誤差 0.00046817160000012237 (0.0172%差)
|
||||||
|
68/25 = 2.72000000, 誤差 0.0017181716000003178 (0.0632%差)
|
||||||
|
49/18 = 2.72222222, 誤差 0.003940393822222443 (0.1450%差)
|
||||||
|
19/7 = 2.71428571, 誤差 0.003996114114285465 (0.1470%差)
|
||||||
|
```
|
||||||
|
|
||||||
|
悪くなさそう(ここから5個ぐらいまでを提示するとして、)
|
Reference in New Issue
Block a user