nasへのバックアップが完了していない事がM1 macbookで数日継続、毎週一回リブートしてますがsonomaへのアップデート後に起きているようなので再度リブートで『遅延』は発生していない模様です。
ちなみにintel MacBookは毎朝リブートしているせいか、このような現象は出ていない。
まあ、ありがちな事象ではあります。
admin
la vie libre
nasへのバックアップが完了していない事がM1 macbookで数日継続、毎週一回リブートしてますがsonomaへのアップデート後に起きているようなので再度リブートで『遅延』は発生していない模様です。
ちなみにintel MacBookは毎朝リブートしているせいか、このような現象は出ていない。
まあ、ありがちな事象ではあります。
admin
Rustももちろん並行処理ができて、そのロジックは他の言語と類似ですが、簡単なコードで確認。Rustのドキュメンtの並行処理を多少改変しています。
どこが改変かというと、スレッドを複数spawnした時の終了待ちをhandleを配列(handles)に入れて、配列からhandleを取り出して全ての終了を待つようにしています。GolangのWaitGroupに相当する機能はなさそうなので、
use std::thread;
use std::time::Duration;
fn main() {
let mut handles = Vec::new();
for i in 0..3 {
let handle = thread::spawn(move || {
for j in 1..10 {
println!("hi number {} {} from the spawned thread!", i, j);
thread::sleep(Duration::from_millis(1));
}
});
handles.push(handle);
}
for i in 1..5 {
println!("hi number {} from the main thread!", i);
thread::sleep(Duration::from_millis(1));
}
for handle in handles {
handle.join().unwrap();
}
}
<実行結果>
hi number 1 1 from the spawned thread!
hi number 2 1 from the spawned thread!
hi number 1 from the main thread!
hi number 0 1 from the spawned thread!
hi number 1 2 from the spawned thread!
hi number 2 2 from the spawned thread!
hi number 2 from the main thread!
hi number 0 2 from the spawned thread!
hi number 1 3 from the spawned thread!
hi number 2 3 from the spawned thread!
hi number 0 3 from the spawned thread!
hi number 3 from the main thread!
hi number 1 4 from the spawned thread!
hi number 2 4 from the spawned thread!
hi number 0 4 from the spawned thread!
hi number 4 from the main thread!
hi number 1 5 from the spawned thread!
hi number 2 5 from the spawned thread!
hi number 0 5 from the spawned thread!
hi number 1 6 from the spawned thread!
hi number 0 6 from the spawned thread!
hi number 2 6 from the spawned thread!
hi number 0 7 from the spawned thread!
hi number 1 7 from the spawned thread!
hi number 2 7 from the spawned thread!
hi number 0 8 from the spawned thread!
hi number 1 8 from the spawned thread!
hi number 2 8 from the spawned thread!
hi number 0 9 from the spawned thread!
hi number 1 9 from the spawned thread!
hi number 2 9 from the spawned thread!
二重ループ処理の複数(三個)のスレッドが動作しています。もちろんスレッドの実行順序は指定できません。
admin
DIで依存関係のあるファイルを定義すると、その依存関係を解決したコードを生成するツールです。これだけでは訳がわからなので以下から実例を。
https://www.asobou.co.jp/blog/web/google-wire
はwireのtutorialをベースに記述しています、https://github.com/google/wire
まず何はともあれwireをインストールします、実はwireはコマンド実行で依存関係を記述したコードを作成します。
% go install github.com/google/wire/cmd/wire@latest
インストールするとパスが通っていれば、% wireで実行できるようになります。
//+build wireinject
package main
import "github.com/google/wire"
func InitializeEvent() Event {
wire.Build(NewEvent, NewGreeter, NewMessage)
return Event{}
}
ここで、NewEvent, NewGreeter, NewMessageが依存関係のある構造体の名前になります。wire.Bildの引数にこれらの構造体の情報を与えると、依存関係を調べて、依存関係を解消するコードを% wire で生成します。(以下)
// Code generated by Wire. DO NOT EDIT.
//go:generate go run github.com/google/wire/cmd/wire
//go:build !wireinject
// +build !wireinject
package main
// Injectors from wire.go:
func InitializeEvent() Event {
message := NewMessage()
greeter := NewGreeter(message)
event := NewEvent(greeter)
return event
}
実は生成されたコードは、元の依存関係を記述しているコード(コメント部分)と同じで、Intialize Eventで呼び出しているだけになります。
func main() {
e := InitializeEvent()
e.Start()
}
/*
func main() {
message := NewMessage()
greeter := NewGreeter(message)
event := NewEvent(greeter)
event.Start()
}
*/
実行するには、
% go buildで実行ファイルを作成すれば、作成されたコードをリンクした実行ファイルが作成されるの、そのまま実行可能です。
このtutorialでは単純なケースなので、それほどメリットは感じられませんが依存関係が複雑になればwireを使うメリットがあるように思います。
admin
現代の言語で多くは関数を第一級オブジェクトとして扱えます、第一級オブジェクトとは言語の持つ基本的な操作を制限なしに実行できることというのがWikipediaでの解説(以下引用)
“第一級オブジェクト(ファーストクラスオブジェクト、first-class object)は、あるプログラミング言語において、たとえば生成、代入、演算、(引数・戻り値としての)受け渡しといったその言語における基本的な操作を制限なしに使用できる対象のことである。”
で、第一級オブジェクトとして関数が扱えれば、それは第一級関数となります。より具体的には以下の使用例を。以下やってることは冗長なだけですが、関数を変数に代入できることは『その言語における基本的な操作を制限なしに使用できる』の一つに該当します。
実際にやっていることは関数の型とポインタを渡しているだけだろうと思いますが。
package main
import (
"fmt"
"math"
)
type calif interface{
calc(i float64) float64
}
type calst struct{
c calif
}
func (c calst)calc(i float64) float64 {
return math.Sqrt(i)
}
func main() {
ci := calst{}
cal := ci.calc(1)
ary := []float64{25, 36, 100, 121, 144}
for _, ft := range ary {
cal = ci.calc(ft) // set func(calc) to variable(cal)
fmt.Println(cal)
}
}
現代の多くの言語では、関数は第一級オブジェクトですが、Rustではそうなっていないようです。意図した理由はあるはずですが、
https://zenn.dev/hinastory/articles/7857427ea390c5
admin
contextは英語の意味は『文脈』ですが、プログラミングにおいては言語に関わらず使われる用語なので、おそらく他に適切な用語はなさそう。
その意味するところはAPI境界を超えて、スレッド間でも共有可能なオブジェクトを提供するといったところかと思います。contextはあたかも一つのスレッドのような動作をするように思います。
contextが一番使われるシーンはJavaでもありますがHttpRequestのようなネットワーク処理でタイムアウトやキャンセルが発生するようなケースに相性が良いから。
以下はcontextでcancel()処理とtimeout()処理、それに何らかの値(key/value pair)を設定してみたもの。
package main
import(
"context"
"fmt"
"time"
)
func main() {
ctx0 := context.Background() // to initialize the context
ctx1, cancel1 := context.WithCancel(ctx0) // to generate cancel function "cancel1"
ctx3 := context.WithValue(ctx1, "key1", 99)
go func(ctx1 context.Context) {
select {
case <-ctx1.Done():
fmt.Println("th1 was canceled")
}
}(ctx1)
ctx2, _ := context.WithTimeout(ctx0, 2*time.Second) // to set the context to timeout mode
go func(ctx2 context.Context) {
select {
case <-ctx2.Done():
fmt.Println("th2 was timeouted", "key value : ", ctx3.Value("key1"))
}
}(ctx2)
cancel1()
time.Sleep(3*time.Second)
}
最後の3秒待ちは値が2秒だとfunc(ctx2)処理が完了する前にプログラムが終了するのでこの値以上が必要です。
channelを使ってclose()処理をしても同じように処理はできますが、
https://zenn.dev/hsaki/books/golang-context/viewer/done
じゃなぜcontextを使うかというと、channelはgoroutine限定ですがcontextは汎用的に使えるからと言うことになるでしょう。
admin
Golangのinterfaceの実装はimplementsのように明示することなく、interfaceで定義したメソッドを全て実装すればinterfaceとしての性質を持ちます。それゆえDependency Injectionも分離したコードが書けるわけですが、他の言語から入ると戸惑いがあるのも事実。
以下の簡単な例で実装時の違いを見てみます。以下のコードではcase 1 ~ 3のいずれかの行を有効にすればコンパイルはできますが、意味はそれぞれ違います。
case 1 : 最も一般的な記述方法、StatStructがinterfaceを実装と認識するのでmain()からinterfaceのメソッドが使えます。
case 2 : 構造体に要素を持たせた場合、この場合はcase 1と同じですが、要素を使わないなら空の構造体にしとくのが自然。
case 3 : このケースとcase 1/2との違いはこの行を記述するだけでStat()の実装がなくともinterfaceを実装したことになること。コンパイルはできますが実行時にStat()が呼べないのでエラーになります。実はこれはDIの手法そのものですが。
package main
import "fmt"
type Stater interface {
Stat()
}
//type StatStruct struct {} // case 1
//type StatStruct struct {a int} // case 2
type StatStruct struct {Stater} // case 3
func (StatStruct) Stat() {
fmt.Println("It's fine today")
}
func main() {
st := StatStruct{}
st.Stat()
}
現実的にはメソッドの実装ではcase 1を使うことが普通だと思います。さらに言えば構造体無しでもStat()を定義すれば動きますが、それはおそらくGoのデザインパターン(ideom)から外れるように思います。
admin
DIはコードをinterfaceを介することでソースコード間の依存性を少なくして、例えばDBの接続種類の入れ替えとかテストの時にスタブに切り替えるとかをコードの修正を最低限で実行できる方法です。結果としてコードの保守性が向上するということになります。
https://free-engineer.life/golang-dependency-injection/
の記事がわかりやすかったのでこれを事例にしてみます。
実際にはVScodeで動かしてみて、パッケージが全てmainでは気持ちが悪いのでパッケージ名は分離しています。以下パッケージ分離版のコードです。
https://github.com/chateight/golang/tree/master/basic/di
<task_usecase(use case.go)のDI実現のキーコード部分>
// Saveメソッドの実装はTaskRepositoryで行っている
type TaskRepositoryInterface interface {
Save(Task) (Task, error)
}
// structのフィールドにTaskRepositoryInterfaceを持たせる
// こうすることで、CreateTaskメソッドでDBへの保存処理を呼ぶことができる
type TaskUsecase struct {
repo TaskRepositoryInterface
}
~~途中省略~~
func (usecase TaskUsecase) CreateTask(title string) (task.Task, error) {
t := task.Task{Title: title}
task, err := usecase.repo.Save(t)
return task, err
}
つまりinterfaceを経由した抽象的な呼び出しにより依存先をinterfaceとすることで、CreateTask中ではinterfaceを呼び出し、結果として実装(ここではsave())と分離がされているので、例えば他の実装との入れ替えが簡単にできるということになります。
Golangの場合には明示的にinterfaceをimplementで明示しなくとも、interfaceで定義した関数のフットプリントと同じ関数が実装されていればimplementされたことになるのでDIの記述のようなケースでも、他の例えばJavaなどの比較すると綺麗にコードが分離できます。Golangはクラシックなオブジェクト指向では無いですが、現代的な抽象化手段を持つ言語です。
admin
toioを動かすにはいくつかの手段がありますが、最も簡単なのはiPadのアプリをインストールして使うScratchの拡張機能としてtoioが使えるtoio Doを使うこと。toio Doはブラウザ経由でも動きますが、まだベータ版なので保存機能とかが無いようでiPadアプリを使うのが一番確実。
さらには、Node.js用のライブラリにtoio.jsというのがあって、
https://github.com/toio/toio.js
このGitHubにはサンプルプログラムもあって簡単に動作確認できます。動作環境はmacbook Air apple silicon M1/Ventura 13です。
上記リンクにサンプルプログラムの動かし方があるので、そのままやってみました。
いくつかサンプルありますが、動画で動かしているのは、
% yarn example:keyboard-control
になります。
async function main() {
// start a scanner to find nearest cube
const cube = await new NearestScanner().start()
// connect to the cube
await cube.connect()
上のコードはサンプルコードからの切り出しですが、cubeへの接続は専用の機能NearestScanner()
が用意されて、電源の入っている一番近くのcudeに接続されるようになっています。package.jsonにある”@toio/scanner”: “^1.0.0″にNearestScanner()も含まれていることになります。
admin
技術情報が公開されていて色々弄れそうなのでtoioを購入、まだ手元には届いていませんが、toioをScratchから使う時にはパソコンとの接続にはScratch Linkを使う(Web Bluetooth API)と言う記述がありました。したがってmicro:bitもコマンドが公開されてるだろうと思って調べてみると、
https://qiita.com/youtoy/items/c98c0996458a21fc1e67
と言う事例がありました。2021年の記事なのでmicro:bit V1向けかどうかは微妙なのでV2では多少変更が必要(少なくともUUIDは別物になる)かもしれませんが。まあ元々ScratchやMakeCodeはどちらもブラウザ上で動作して、ブラウザとmicro:bit間の通信手段はScratch Linkなので中を通るデータは変わってもtoioでも使えるのでしょう。
上記のサンプルはパソコン側での受信なので、パソコンからの送信は、
https://qiita.com/yagshi/items/abfb3717fb978d031b7e
があります。
いずれにしてもbluetoothとのブラウザからの接続はWeb Bluetooth APIを使いますが、ブラウザが限定でChromeがメジャーブラウザです。
過去記事:https://isehara-3lv.sakura.ne.jp/blog/2021/04/01/microbitでbluetooth通信/
C/C++で低レベルでの接続には、
https://tech.microbit.org/software/runtime/
が関連ドキュメントになりそうですがmicro:bit V2はCODALというハードウェアの抽象化手段があるようです。
admin
Rustの場合、コミュニティがしっかりしていてドキュメントの整備もきちんとされているので、特別にテキストを購入する必要はなさそうです。
https://doc.rust-jp.rs/book-ja/title-page.html
の15章あたりまで、ダラダラと読んでますが、例は
https://doc.rust-jp.rs/rust-by-example-ja/
こちらを使えば、一通りの理解はできるように思います。
結局のところ、Rustの仕様のかなりの部分はメモリ管理をコンパイラが判断できるようにユーザーが記述するコードで指示することに集約されるだろうと思う。したがって堅牢性と実行速度を要求するOSやアプリケーションにはRustを使うと言う選択になるんだろう。組み込みやIoTなどで小規模な開発では、コードが冗長になりがちなので、Rustである必要性はあるのかと言う感覚はあります。
admin