標準愚痴出力

個人的なIT作業ログです。もしかしたら一般的に参考になることが書いているかもしれません(弱気

葛󠄀城市の「葛󠄀」の字

この動画で葛󠄀城市の「葛󠄀」の字がややこしいことになってしまったことを知った。

二つの字体があるんだなぁ

少なくとも現在のブラウザで表示分けが出来るのが幸いかな。

( しかし、フォントが小さいターミナルだとどっちか分かりづらい。最初「L人」が表示されてると思って拡大したら「ヒ」だったりすることもあったし )

rangefunc の練習:Yet Another Scanner

  • 既存のライブラリに bufio.Scanner があるが、改行をカットしてしまう
  • 改行を含めて読み取りたいときは (*bufio.Reader) ReadString('\n') を使う
    • EOF を検出した時の処理が面倒だが、雑に書くと最終行が失われてしまう

面倒なく、ありのままに読み込むコードを楽に書くための iterator を書いた

foo.go

package main

import (
    "bufio"
    "fmt"
    "io"
    "os"
)

func readlines(r io.Reader) func(func(string, error) bool) {
    return func(yield func(string, error) bool) {
        var br *bufio.Reader
        if _br, ok := r.(*bufio.Reader); ok {
            br = _br
        } else {
            br = bufio.NewReader(r)
        }
        for {
            line, err := br.ReadString('\n')
            if err != nil && err != io.EOF {
                yield(line, err)
                return
            }
            if !yield(line, nil) {
                return
            }
            if err == io.EOF {
                return
            }
        }
    }
}
// 以上がライブラリコード
// 以下、使用例

func mains() error {
    for line, err := range readlines(os.Stdin) {
        if err != nil {
            return err
        }
        fmt.Print(line)
    }
    return nil
}

func main() {
    if err := mains(); err != nil {
        fmt.Fprintln(os.Stderr, err.Error())
        os.Exit(1)
    }
}

サンプルファイル foo.txt (制御コードが分かるよう、バイナリエディターのアウトプットで)

$ binview.exe  foo.txt
binview snapshot-windows-amd64 by go1.17.7
00000000 41 42 43 0D 0A 44 45 46 0D 0A 47 48 49          ABC←↓DEF←↓GHI
 [UTF8]  70='\x46'(1/1:U+0046) @ 7=0x7/13=0xD

実行結果:

[1] <DESKTOP-NPOTG52:~>
$ env GOEXPERIMENT=rangefunc go run foo.go < foo.txt
ABC
DEF
GHI[1] <DESKTOP-NPOTG52:~>
$

もう一つだけ、小さな optional パッケージを作った

以前:

というパッケージを作ったが、別の方式で、もう一つ作ってみた。

README にだいたい書いてあるけれども、go-minimal-optional では

type Value[T any] []T

と表現していた構造を

type Value[T any] struct {
    value T
    ok    bool
}

という安直な形で再実装してみた。メモリアローケーションが改善されるのではないかと期待してのことだが… 結果は

go-minimal-optional

BenchmarkIfSome-4       656701584                1.790 ns/op           0 B/op          0 allocs/op
BenchmarkIfNone-4       1000000000               0.7019 ns/op          0 B/op          0 allocs/op

go-tiny-optional

BenchmarkIfSome-4       675800485                1.787 ns/op           0 B/op          0 allocs/op
BenchmarkIfNone-4       1000000000               0.7008 ns/op          0 B/op          0 allocs/op

ほんの少しだけ速いけど、微粒子レベルの差にすぎず、あまり変わりませんねぇ。それよりも、allocation 回数がどっちでもゼロだったのに驚いた。Go の最適化がうまいこと効いていたみたい。

まぁ、これくらいなら、1.22 でなくとも「rangefunc もどき」が使える go-minimal-optional でいいかもしれませんねぇ。

URL をスクランブルする

とほほのJavaScriptリファレンス - とほほのWWW入門」を読みながら、URL文字列を前後ひっくりかえして、リンク元を検索しづらくする JavaScript を書いた (JavaScript を書くの、20年ぶりくらいかな?)

テキストボックスに適当な URL、たとえば:

を入力すると、ダイアログボックスで

というURL が提示される。この URL をクリックすると

次のURLへ移動します。問題なければクリックしてください。

https://twitter.com/elonmusk/status/1767108624038449405

というページを表示する。

なお、本ツールは怒られたら手動的に消滅する。

Go で適当な btree を検討していたけど、tidwall/btree がいいかもしれないなぁ

既存のメソッド Scan の仕様が range から呼ばれた関数の要求仕様にちょうどマッチしてた。

package main

import (
    "fmt"

    "github.com/tidwall/btree"
)

func main() {
    var users btree.Map[string, string]

    // add some users
    users.Set("user:4", "Andrea")
    users.Set("user:6", "Andy")
    users.Set("user:2", "Andy")
    users.Set("user:1", "Jane")
    users.Set("user:5", "Janet")
    users.Set("user:3", "Steve")

    // Iterate over the maps and print each user
    for key, value := range users.Scan {
        fmt.Printf("%s %s\n", key, value)
    }
}
$ env GOEXPERIMENT=rangefunc go run main.go
user:1 Jane
user:2 Andy
user:3 Steve
user:4 Andrea
user:5 Janet
user:6 Andy

tidwall さんは、transform というフィルターパッケージの開発者で、よいプロダクトをいろいろ作ってる人なので、もうこれでいいかなぁ感 ( これのSTAR数も前に確認した2022-04-30時点から、680→982 に増えてる )

twitter、もとい、X にバッチファイルのコードの類を貼る時にリンク化されないテク

echo off を示す @ マークが mention 扱いされてしまう

@setlocal
set "TARGET=%1"
set "TARGET=%TARGET:\\?\=%"
"%EDITOR%" "%TARGET%"
@exit /b %ERRORLEVEL%

これを twitter へ貼ると、@exit が exit さんへの mention になってしまう @ の後に空白を入れればよい。また、バッチファイル本体で @ の後に空白があっても誤動作はしない。

@ setlocal
set "TARGET=%1"
set "TARGET=%TARGET:\\?\=%"
"%EDITOR%" "%TARGET%"
@ exit /b %ERRORLEVEL%

github の URL を引数に与えるコードがリンク化されてしまう

scoop によるツールのインストールを説明するとき

C:> scoop bucket add hymkor https://github.com/hymkor/scoop-bucket
C:> scoop install csview

URL部分がリンク化されてしまう。これを避けるには

C:> scoop bucket add hymkor https://github"."com/hymkor/scoop-bucket
C:> scoop install csview

と URL で認識できなくなる場所に二重引用符を挿入してやればよい。

github\.com と書く人もいるが、シェルに渡す場合、コマンドに渡される時にこの \ は消えてくれない。二重引用符であれば間違いなく消えてくれるので、そのままコピペして使用可能だ。

(解決編) jujutsu v0.15.1 で、コミットログを gvim.exe で編集できなくなってしまった

(前回) → jujutsu v0.15.1 で、コミットログを gvim.exe で編集できなくなってしまった。 - 標準愚痴出力

どうやら、\\?\C:\… 形式のパスに含まれるクエスチョンマークを、gvimワイルドカードと誤認してしまったらしい。自前展開にトライした結果、失敗したようだ。

結論としては、gvim--literal オプションをつけて、ワイルドカード展開を抑制すればよい。環境変数EDITORやJJ_EDITORではなく、jj config edit --user で以下の設定を追加した。

[ui]
editor = [ "C:/Users/hymkor/scoop/apps/vim/current/gvim.exe" , "--literal" ]

しかし、なぜ \\?\C:\… が使われるようになったのだろう。どうも、Rust のパスの正規化関数の仕様らしい。

use std::fs;

fn main() -> std::io::Result<()> {
    let path = fs::canonicalize("C:/Users/hymkor/.vimrc")?;
    println!("{}",path.display());
    Ok(())
}
$ rustc c.rs
$ c
\\?\C:\Users\hymkor\OneDrive\Share\Etc\.vimrc

\\?\C:\... 形式へ変換されると共に、シンボリックリンクも展開されている。なるほどなぁ