日記(2025年1月18日)

Sat Jan 18 10:32:41 JST 2025 (modified: Sat Jan 18 10:40:12 JST 2025)
views: 834, keywords:日記

 1月は期末の発表&卒論シーズンなので各学年のポスターやスライド、卒論、修論の添削の毎日です。で、これはとても疲れる(まだ一人前とは言えない文章を精読して、様々な感情を抑えて、本人にとってためになるアドバイスをするという地獄のような)作業なので、合間に現実逃避するようにコードを組んでます。んで、コード書くだけなのでリリースしたり作業記録をつけたりする作業がすべて後回しになってるのでメモしときます。

学会で発表した動く物体の位置予測アルゴリズムのROS 2移植

 ↓このときに発表したアルゴリズムをROS 2で使えるようにしています。ロボットから見えてるものが数秒後にどこにいくか予測するアルゴリズムです。写真中でモザイクかかってるポスターはこちらで公開中

C++で画像ファイルを読み込んで画像ファイルを出力するコマンドとして作っていたのを、RustでROS 2のパッケージとして実装しなおしています。

READMEを作っていますが、まだちょっと直すのでついった上ではできたといってません。また、いまのところロボットが静止していることを前提にして作っています。ロボットが動けるものは別のパッケージにしようと思います。現状、出力はこんな感じ↓です。2〜10秒後に移動しているものがいそうな位置の分布がグレースケールで表示されています。

これでも歩いてる人に雪玉投げるときの目標位置の計算くらいには使えるような気がします。(ダメです。)

自作シェル

 自作シェルのほうは、年末辺りからBashのヘビー級補完機能パッケージのbash-completionを動かそうと格闘中してました。この作業は遅いながらも少しずつ進んでいます。bash-completionはニッチな機能を使いまくってるシェルスクリプトの塊なので、ニッチな機能を少しずつ実装して動作確認しながら作業を進めています。

 もうひとつの作業として、エラーハンドリングをちゃんとしようということで、いままでboolやOptionで成否を返していたのをRustのResult型を真面目に使うように変更中です。例として、

$ ( echo abc ; echo def ) | rev
   cba
   fed

のような()で複数のコマンドをまとめるサブシェルをパースする関数を図1に示します。Resultを使う前は、図1のように、カッコの中身に文法エラーがあったら「None」を返すだけでしたが、これだと中身にどんなエラーがあったかまでは返せません。そのため、このコードの場合はeat_inner_scriptのなか(あるいはさらにそこから呼ばれている関数)でエラー処理せざるを得ず、エラー処理のコードが散らばってました。

  • 図1: Result使う前(説明に不要な部分はカット)
//Result使ってないやつ
   pub fn parse(feeder: &mut Feeder, core: &mut ShellCore, substitution: bool) -> Option<Self> {
       let mut ans = Self::default();
       if command::eat_inner_script(feeder, core, "(", vec![")"], &mut ans.script, substitution) { //カッコの中身を取り出す関数
           Some(ans) //中身に文法エラーがなかったら、パース結果をなにかあったという印のSomeにくるんで返す。
       }else{
           None //エラーがあったら「何もない」を返す。
       }
   }

 これを図2のように関数の戻り値の型を変えました。元々の型Optionは、結果(ans)がある場合にSome(結果)、ない場合にNoneを返す型ですが、それをさらにResultでくるんで、次の値を返すように変えています。eat_inner_scriptの戻り値の型もResultを使うように書き換えてあります。

  • カッコの中身に文法エラーがない: Ok(Some(ans))
  • カッコの中身に文法エラーがある: Err(エラーの原因)
  • カッコの中身に文法エラーがないけどeat_inner_scriptNone(正確にはOk(None))を返してきた: Ok(None)

これで、エラーがこのパースの関数の呼び出し元に伝わるようになります。コードについてはそんなに変更点がなく、返り値をOkでくるみ、さらにeat_inner_script関数の呼び出しのうしろに?をつけるだけです。?があると、関数がErr(...)を返してきたときに、即座に関数が終わってErr(...)が返るようになります。Ok(...)の場合はOkを剥がしてくれます。便利です。

  • 図2: Result使った後
//Result使ったやつ
   pub fn parse(feeder: &mut Feeder, core: &mut ShellCore, substitution: bool) -> Result<Option<Self>, ParseError> {
       let mut ans = Self::default();                                            //↑Result型の導入
       if command::eat_inner_script(feeder, core, "(", vec![")"], &mut ans.script, substitution)? { //?をつける
           Ok(Some(ans)) //Okで囲む
       }else{
           Ok(None) //Okで囲む
       }
   }

 ただ、「コードについてはそんなに変更点がなく」と書きましたが、パーサーを構成する全部の関数の型を変更して、かつ整合性があるようにコードを書き換えなければならなかったので地獄でした。でしたというか継続中です。話がややこしくなると思って連載当初に使うのを躊躇したのを後悔してます。

 連載については、たぶん6月号あたりから?を使ったコードに移行すると思われます。

とりあえず現場からは以上です。

ノート   このエントリーをはてなブックマークに追加 
 

prev:2024年はこんなの書いた next:2/15(土)にシェル芸勉強会やります(ちょっと内容変えます)

やり散らかし一覧

記事いろいろ