rch850 の上澄み

技術的な話題とか、雑談とか。タイトルを上澄みに変えました @ 2020/09/02

新春シェル乗り換えショー 〜 fish から zsh prezto へ

長いこと fish shell を使ってきたんですが、この春から zsh prezto に乗り換えました。

いつから fish 使っていたかはっきり覚えてないんですが、Twitter で最古の言及が約5年前。それぐらい長い付き合いだったんだと思います。

この手の環境は、あまりヘビーにカスタマイズせず使いたいなと考えていて、その結果が fish という選択でした。

fish のよかったところ

  • 素の状態でいい感じだった。
  • 補完がいい感じだった。
  • コマンド履歴を雑に上キーで遡れた。bash だと Ctrl+R を駆使するところ。

自分では使いこなせなかったところ

しばらく時が経って、macOS でのデフォルトシェルが zsh になりました。さらにこの春、prezto を使えばかなり fish っぽくなると同僚から教わりました。これがとどめでした。

実際に使ってみると、fish のよかったところが満たされていて、気持ちよく使えています。カスタマイズ具合はぼちぼちです。

  • 追加モジュール。各モジュールの説明は prezto/modules
    • autosuggestions
      • 補完が fish っぽくなる。この薄いやつ。
      • f:id:rch850:20210415014941p:plain
    • git
      • アグレッシブなエイリアスzstyle ':prezto:module:git:alias' skip 'yes' でオフ
    • history-substring-search
      • コマンドを遡るのが fish っぽくなる。
  • しばらくいろいろなテーマで遊びたいので zstyle ':prezto:module:prompt' theme 'random'

あ、あとこの typo 修正もデフォルトである機能みたいで素敵ですね。

% vrew
zsh: correct 'vrew' to 'brew' [nyae]? n
zsh: command not found: vrew

以上 fish から prezto に乗り換えた記録でした。

参考記事:

Android の ML Kit とスクリーンキャプチャのメモ書き

Android で画面をキャプチャしながらいい感じにテキスト抽出してみたいな、と考えて、色々試してみました。Android 開発の基礎力がないので、つぎはぎなんとかならないかと調べてみた、雑多なメモ書きだと思ってください。

ML Kit でのテキスト検出

developers.google.com

この ML Kit のドキュメントを見ながら、自力で空のプロジェクトに追加してみようとしたのですが、Android 開発の基礎力がなさすぎて断念。

冒頭に codelab へのリンクがあったので、そちらに頼ることにしました。

codelabs.developers.google.com

こちらはコードをダウンロードして starter に少しコードを書き足すだけだったので、問題なく動いてくれました。やさしいですね。コードは Java なので、Kotlin プロジェクトに適用するときは色々と変わるところがありそうです。

画像を選択するところに選択肢を増やして、読み取らせることもできました。

Media Projection API でのスクリーンキャプチャ

キャプチャについては公式のサンプルに頼ってみました。動くことが分かっているものに手を入れていくスタイルのほうが、初心者にとってはやさしいと思うので。

github.com

この ScreenCapture というサンプルが、 Media Projection API を使ってキャプチャするサンプルです。難なく動きました。

組み合わせ

このサンプルだけでは、キャプチャして画面に表示するだけなので、ML Kit に流し込むため Bitmap にする方法を調べました。

techbooster.org

TechBooster さんの記事、もう6年前ですが、これで大体の流れは分かりました。結局 Codelab のサンプルに、この記事を参考にしながらスクリーンキャプチャを組み込む実装となりました。

Bitmap にするときに次のようなエラーが出てしまったのですが、PixelFormat.RGBA_8888 にすればよいという記事があったので、その通りやってみたらエラーは出なくなりました。

E/ImageReader_JNI: Producer output buffer format: 0x1, ImageReader configured format: 0x4
D/AndroidRuntime: Shutting down VM
E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.example.android.screencapture, PID: 11064
    java.lang.UnsupportedOperationException: The producer output buffer format 0x1 doesn't match the ImageReader's configured buffer format 0x4.
        at android.media.ImageReader.nativeImageSetup(Native Method)

ここまでの実装だと、他のアプリがフォアグラウンドになっているときのキャプチャができませんでした。フォアグラウンドサービス化などを試してみたいと思います。

TypeScript 4.2 の Template Literal Expressions Have Template Literal Types の例

TypeScript 4.2 の Template Literal Expressions Have Template Literal Types について。これは `/topics/${id}` が TypeScript 4.1 までは string 型だったのが TypeScript 4.2 から `/topics/${string}`型になったという話です。idnumber であれば `/topics/${number}` 型にもなります。

実例。よくある API アクセスで、パスから戻り値の型を自動判定します。

export interface Topic {}

async function fetchJson(path: `/topics`): Promise<Topic[]>;
// ${string} は ${number} でも構いません
async function fetchJson(path: `/topics/${string}`): Promise<Topic>;
async function fetchJson<T>(path: string): Promise<T>;
async function fetchJson<T>(path: string): Promise<T> {
    return (await fetch(path)).json() as Promise<T>;
}

// これは 4.1 でも 4.2 でも Topic[] 型
const topics = await fetchJson('/topics');

// これは 4.1 でも 4.2 でも Topic 型
const topic300 = await fetchJson('/topics/300');

// TypeScript 4.1 だと unknown 型なのが
// TypeScript 4.2 だと Topic 型になる
const n: number = 10
const topicN = await fetchJson(`/topics/${n}`);

表にするとこうなります。

4.1 4.2
'/topics/300' `/topics/${string}` `/topics/${string}`
`/topics/${n}` unknown `/topics/${string}`

便利そう〜

web.dev に PR を出して Eleventy と Alex について学んだ

TL;DR

  • web.dev の記事は Eleventy で生成していて {% Banner 'caution, 'body' %} といった shortcode を使える。
  • web.dev は inclusive な用語になるよう alexGitHub Actions で動かして自動チェックしている。

きっかけ

スマホで web.dev の How to use HTTPS for local development を読んでいたら、Caution と書いてある場所に何も書いてなくて、まさかと思って端末を横持ちにしたら内容が出てきた(現在は修正済み)。

ページ下部の "File a bug" から不具合報告することもできたけど、ちょっとコードを読んだら直せそうだったから、直して PR を出して、ありがたいことにマージされた(その後、別の PR でもっといい形にしてもらった)。

github.com

{% Banner %} と Eleventy の shortcode

どこをどう直せばいいかは、こういう流れで調べた。

  • このページのソースを見に行って Caution 周りのコードを見たら {% Banner 'caution' %} と書いてあった。
  • コード全体を Banner で検索したら、常に表示すべきところは {% Banner 'caution', 'body' %} となっていた。
  • Banner.js を見つけて大体の動きを察した。

というわけで {% Banner 'caution' %}{% Banner 'caution', 'body' %} にして PR を出した。

この書き方でなぜこういう動きをするのか、後から調べてみた。

  • ビルドを受け持っていると思われる continuous-integration-workflow.yml を見たところ、ビルドは npm run build を呼び出すシンプルな形。
  • package.json を見ると build は "npm-run-all clean sass gulp rollup eleventy sw-manifest" で、eleventy がそれっぽい。ここで初めて Eleventy という Static Site Generator (SSG) の存在を知った。なんとトップページの Build with Eleventy に web.dev が載っている。知ってれば知っていたやつですね。
  • .eleventy.js を読んだら config.addPairedShortcode('Banner', Banner); とあった。
  • さらに調べると Eleventy に shortcode という仕組みがあることが分かり、 {% Banner 'caution', 'body' %} を div に変換していることが分かった。

alex での文章チェック

PR を出した後、Netlify で自動的にステージング環境ができたのは、まぁよくある話だなと思ったんだけど、Alex Recommends Report というものが初見だった。

f:id:rch850:20210207165223p:plain

例えばこのような指摘が出ていたので、master を main にする的な話なんだなというのはなんとなく分かった。

special may be insensitive, use has a disability, person with a disability, people with disabilities instead

Alex と言われても macOSsay コマンドで指定する英語の男性しか思いつかなかったので、こちらもそれっぽい GitHub Actions を探したところ、alex-workflow.yml があった。ここで使っていたのが Alex Recommends という Action だった。この Action は alex というものを走らせるものだった。textlint は知っていたけど、これは特に inclusive を狙ったもののようだ。もしかして、と思って調べたら textlint-rule-alex があった。

以上、web.dev に PR を出したときに学んだことでした。

不要になったコードの削除

フロントエンドでの「この画面が不要になった」、バックエンドでの「このAPIが不要になった」といったケースでの、不要になったコードの削除について、考えをまとめてみた。

なぜ削除するのか

  • なぜ残っているのかという疑問が発生するのを防ぐため。
  • リファクタするときの邪魔にならないように。
  • 全文検索で無駄にヒットするのを防ぐため。
  • ビルド、テストの時間を短縮するため。
  • 総じて言えばメンテナンスコスト削減。

色々理由を挙げてみたなかで、疑問を防ぐことを1番目に持ってきた。この理由は、他の要素と違って我慢や努力で解決できないからだ。

会話例:

  • 「icon_plus.png ってもう使ってない?」
  • 「使ってなさそうだな。ファイル名で検索してもヒットしない」
  • 「じゃぁなんで残ってるんだろう」
  • tig -Sicon_plus してみたけど、このコミットでの削除漏れかなぁ。うーん、たぶん消して大丈夫でしょう」

icon_plus.png を使わなくなった時点で消していれば、このやり取り自体が発生しなかったはずだ。スムーズに、気持ちよく仕事をするために、不要なコードはなるべく削除されていたほうがいい。

根から断つか、葉から断つか

根とは、フロントエンド (SPA) やバックエンドではルーティング部分だと思ってもらえればいい。逆に、葉は、フロントエンドの場合はその画面でしか使ってなかった画像、バックエンドの場合はその API でしか使ってなかったユーティリティ関数といったものだ。

根から断つか、葉から断つか。多くの場合、削除しようと思いたつきっかけが根にあるので、できる限り根から断つほうがいいと考えている。このユーティリティ関数を消したい。と思い立った場合は、それを根とみなす。

根にハサミを入れる

この根を断つ、と決めたら、まずそこにハサミを入れる。ルーティング情報を削除するといった具合で。URL /hoge に対してこの画面、この関数といった連携を断つ。もし、その画面や関数が一気に消せるようなボリュームじゃない感じがするなら、そこに「TODO: これはもう使ってないので、あとで消します」のようにコメントしておく。そして、ここでコミットして一息入れる。

このコメントをさぼると、いろいろあって削除しきれなかったときに「なぜ残っているのかという疑問」が発生してしまう。このコメントとルーティングの削除が同じコミットに入っていれば、後から経緯を調べるのは格段に楽になるはずだ。

少しずつ削除していく

あとは、根を断って浮いた部分を削除していくだけだ。削除するときも、根の方から削除していくのがやりやすい事が多い。葉が明らかに見えていて、削除も簡単なら、そっちから削除してもいい。

根の方から少しずつ削除していくと、最終的に葉まで至る。フロントエンドなら、不要となったCSS、画像など。また、葉と言えるボリュームではないけど、その画面だけで使っていたライブラリなんかも末端の葉だ。バックエンドの場合は、データベースのテーブルやカラムが葉となりうる。こういった末端の葉ほど、削除し忘れたときに「なんで残ってるの?」という疑問が発生しやすいので、できるだけ削除する。

もしかしたら、削除しないほうがよさそうなコードなどがあるかもしれない。その場合は、「これはもう使ってないけど、近いうちに使う予定があるから残しておく」といった、削除してない理由を記録して、後日疑問が出ないようにする。とにかく「なぜ残っているのかという疑問」を残さないようにする。「なぜ」を調べるコストは高い。

PR やコミットの粒度

できれば、最初のハサミを入れた時点で PR にしておくといいと思う。というのも、万が一、その画面や API が使われていて、戻さなければならなくなった場合に、ハサミを入れただけなら復旧が簡単だからだ。その先についても、復旧のことを考えて PR の粒度を考えるといいだろう。

コミットの粒度は、それなりに小さめに留めておいたほうがいいように思う。よほどのことがない限りい、小さくて困ることはない。1コミットにつき、多くて数ファイル、数百行か。とはいえ、できるだけ意味のある単位、コンパイルが通る単位でまとめたほうがいいので、場合によっては数十ファイルなどに及ぶこともあるだろう。そのあたりはいい感じにしよう。

コミットを小さくするのと同じ理由で、PR を小さくしてもいいのでは?とも考えられるが、PR を小さくする場合は、その PR の先に控えている作業を記録したり記憶したりしておかないと、削除漏れが発生しやすいので注意が必要だ(理由はちょっと長くなりそうなので割愛)。

放送大学の「統計的因果推論の考え方と技術」を見た

BS で、放送大学「統計的因果推論の考え方と技術」の2回目以降を見たら、本当に何もわからない状態から、ある程度、用語や考え方がわかるようになった気がします。

1月31日(日)の 12:45 から 18:45 にかけて一挙放送があるので、ちょっとでも興味がある方はぜひ録画しておきましょう!

なお、講師は書籍「統計的因果推論」の著者の岩崎先生です。

www.amazon.co.jp

視聴の経緯

ここ1年ぐらい、因果推論が気になるけど勉強してなかったところ、Twitter でたまたま奥村先生のこのツイートを見かけたので、見てみることにしました。

このツイートを見たのはとても運が良かったです。気づくのが若干遅れて、2日目からの視聴でした。

なお、自分自身の因果推論パワーはというと、さっき書いた書籍「統計的因果推論」は持ってないけど、効果検証入門Kindle で買って、最初の少しだけ読んだ状態でした。この放送で出てきますが、「独立の記号は ⊥」と聞いて「そういえばそうだった、きがする」程度です。ベイズは少しかじってたので、条件付き独立の書き方は知ってました。

全体的な感想

数式、図、実例、口頭での説明のバランスが良かったのか、個人的にはスルスルと頭に入ってきました。とだけ書くと簡単そうですが、簡単ってわけではないです。予備知識がないせいか、個人的には、メモを諦めて視聴に集中すればなんとかついていけるペースでした。まずは視聴に集中して、気になるところは録画で一時停止しながらメモを取っていました。

内容については、細かく書くとキリがないので、考え方として理解できてよかったなと思う3つを書いておきます。

  • A さんが広告を見たら買う/買わない Y_i(1)。A さんが広告を見なくても買う/買わない Y_i(0) を、潜在的アウトカムアプローチ \{Y_i(1), Y_i(0)\} という考え方を使って表現する。これを活用して本来測れない数字を推定したりする。
  • 「広告を見た A さん、B さんが買う確率は50%」という考え方ではなく「A さんは広告を見たら買う」「B さんは広告を見ても買わない」というのは決まっていて、確率的なのはサンプルとして A さんが選ばれるか B さんが選ばれるかだという考え方をする。
  • 実験するなら、年齢、性別、居住地などが偏らないように(共変量が同じ個体を処置、対照で均等にする)。共変量がたくさんあって、完全一致する人なんていないよ、というときは、傾向スコアという手段を使うこともできる。

こんな考え方をベースにしながら、いろいろな手法を紹介していく内容でした。最終日は「薬を飲みなさいね、といった人が飲まなかったとき、それが効果の推定にどう影響するか」なんてことまで気にした話が出てきます。

本を読むよりテレビで見たほうが頭に入ってくるかも、という方はぜひ一挙放送を見てみてください。

シェルを spacefish から starship へ

久々に開発環境保守。

Catalina で zsh がデフォルトになってから1年が経って、fish を離れてデフォルトの zsh で生活しようかという気持ちが少しだけ芽生えた。

  • なるべく環境に対して複雑な設定をしたくない。
  • fish は複雑な設定をせずにいい感じにしてくれるけど、そもそも fish を入れる時点で環境に対してだいぶ手を入れている感じがある。
  • zsh で fish ぐらいの便利さを手にしようとすると、結構手を入れなきゃいけない気がする。試してないけど。
  • じゃぁやっぱり fish でいいや。

ということで fish 路線を続行。

fish では spacefish を使っていた。過去ログさかのぼったら、意外とそんなに昔じゃなくて、去年の3月だった。最近どうなってるかなと見に行ってみたら……

f:id:rch850:20201114023320p:plain

どうやら Starship というものが後継となったそうで。

Starship を試すために、まずはいったん spacefish を削除。これまでありがとう。*1

fisher rm matchai/spacefish

Starship のガイドを見ると、まず Prerequisites として Nerd Font を入れましょうとある。前々から ligature が気になっていた FiraCode の Nerd Font 版をインストール。インストールには Homebrew を使いました(手順)。

brew tap homebrew/cask-fonts
brew cask install font-fira-code-nerd-font

そして Starship 自体もインストール。

brew install starship

いったん starship init fish | source してそれっぽく動いたので config.fish に設定を追加。これにて完了!

……だったんですが、メールアドレスが出るのがなんとも言えない気持ち。

f:id:rch850:20201114024406p:plain

そもそもこのメールアドレス何?ってことで、いろいろ調べてみた結果、GCP の設定ファイル .config/gcloud/configurations/config_default に書いてあるメールアドレスをいじったら変わったので、どうやら GCP の設定を表示しているようです。

スクショを撮った時にふせなきゃいけないのはちょっと面倒だな、と思ったので、対処方法を調べました。

あまり難しい話ではなく、設定方法にある通り ~/.config/starship.toml を編集すればメールアドレスが出なくなりました。めでたしめでたし。

*1:fish のプラグイン管理には fisher を使ってます。