Ubuntu 22.04にアップデートした

トラブルは特になく自前でコンパイルしていたソフトウェアの emacsと h2oを再コンパイルしたぐらい. h2oはバンドルしている mrubyが古く Ruby 3では mruby handlerを有効にしてビルドできないため, Ruby 2が必要になった.

github.com

Ubuntu 22.04の Rubyは 3.0系になっているので, 一時的に Rubyを 2.7.6をインストールしてそれを使ってビルドするようにした. (Ruby 2.7.6の opensslが OpenSSL 3.0系に対応していなくて警告が出るが特に問題はなかった)

再インストール後 https://syohex.org/ も無事に動いた.

List::UtilsBy::XS 0.06をリリースした

metacpan.org

github.com

Debugビルドした Perlだと extract_by で assertionに引っかかることがあった問題の修正. 戻り値の個数が引数の数より多い場合 EXTEND を使って Stackを確保しないといけないという理解. 他のメソッドはなんで引っ掛からないんだって思ったけど引数の数と戻り値の数が同じ場合は問題ないようである. どちらも stackを使って渡されておりその分確保されているためだと思われる

Perlたまにテストが通らないとか動かないとかのレポートを受けてコード見るけど, XSはマジでわからん. 他の言語の C言語拡張も書いたことがあるけど, ここまで VMの構成を意識しないといけないのはないよなと思う. まあ最初期のもので他言語をそれを見て扱いやすいものにしたのだと思うけど. 今回は Perl coreにバンドルされるソースに似たものがあったので何とかなったという具合でした.

世界一やさしい海外移住の教科書

最近英会話を始めたりしたりして海外移住、海外で働くというのが幾分気になっているため読んでみた。1ページ当たりの情報量が少ないのですぐに読めました。大まかに情報を知るという意味では悪くなかったけど、そこの裏にあるであろう苦労があまりに見えなさすぎるのが気になった。個人的にはどれぐらい大変なことがあって時間かける必要があるというのが知りたかったのですがそういう本ではなかったです。「やさしい」と銘打っているのでそこはあまり触れずということなのかもしれないけど気になった。例えば英会話の習得にフィリピンなどへの短期言語留学が出てくるけど、調べるとうまくいかなかった話も少なからず出てきてそこまでお手軽ではないのではないかという気がした。

まあ海外移住をそこまでハードルの高すぎるものと考えすぎず, チャレンジすることが大事ということとして受け取っておきます。

F#を4ヶ月ほど学んでみて

今年に入って新しい言語を学ぶかということで F#の勉強を初めて 4ヶ月ほど経ったので現状のまとめ。 やったことは主に小さいプログラムを書いたり, LeetCodeの問題を 100問ほど解いたという程度(LeetCodeは現状 F#未対応なので対応してほしい)。

結論としては始めるのが楽で学ぶ価値のある言語だと考えています。

書いたもの

良い点

  • セットアップが楽
    • .NET SDKを入れて PATHが通って入ればどの環境(Win, macOS, Linux)でも始められる。あとはVSCodeに Ionide拡張を入れるだけで OK.
  • 1ファイルからコードを書ける
    • いちいちプロジェクトを作らなくても .fsxファイルを作って書き始められるのが良い
  • REPLベースの開発
  • Java系言語に比べるとだいぶ軽い
    • 気楽にやるにはすぐに REPLなど立ち上がってほしいので.
  • 標準ライブラリの APIシグネチャの統一感
    • パイプライン演算子を使うことが意識された引数の並び。
  • ライブラリの豊富さ
    • .NETはライブラリが豊富

いまいちなところ

現状特に不満はないですが、しいてあげるなら

  • 公式ドキュメントが複数ヶ所にある
    • 言語のドキュメントは Microsoftにあるけど, 標準ライブラリのドキュメントは githubにあったりと一つの場所にまとまっていない
  • Ionide拡張の REPL連携
    • Emacsだったらカーソル位置の関数を REPLに送るとか様々な単位で REPLに送れるのだけど, 行かリージョンかファイル全体かと大雑把な区切りしかない。特にカーソル位置の関数は欲しいかな
  • formatter
    • fantomasというのがあり, それを使えばいいのだけど無駄に空行があったり改行があったりあまり好きじゃない。まあ統一されているのが重要なので大したことはないのですが。

言語を学んで

普段は C++とか Rustとか C#を書いているのですが, immutable dataが基本でループでなく再帰を使うというのは良い刺激になっていると思います。 副作用を使えると一つのことにいろいろ詰め込んで書けてしまうけど, そうではなく副作用がないように分割して考えるということを意識するようになったように思えます。現状アルゴリズム的なものを中心に書いてきたので今後はアプリケーションなり実用的なものを書いていければなと思っています。

F#で外部コマンドを起動し, その標準出力結果を扱う

コード

open System.IO
open System.Diagnostics

let cmd = new Process()
cmd.StartInfo.FileName <- "ls"
cmd.StartInfo.Arguments <- "-l /usr/bin"
cmd.StartInfo.UseShellExecute <- false
cmd.StartInfo.RedirectStandardOutput <- true

if cmd.Start() |> not then
    failwith "Failed to execute command"

using (new StreamWriter("tmp.txt")) (fun writer ->
    cmd.OutputDataReceived.Add(fun e -> writer.WriteLine(e.Data))
    cmd.BeginOutputReadLine()
    cmd.WaitForExit())

C#と同じではあるのだけど。ハマりどころとしては event(OutputDataReceived)を使わないと出力が大きい場合 4096 byteしか読めないという問題があった. ただ行単位かつ改行コードは stripされた状態でイベントハンドラーに渡ってくるので微妙な感じは少しある. 今は dotnetクロスプラットフォームだけど プロセスの扱いとかは Win32 APIにめちゃ引っ張られているなぁという感じがする.

F#で Priority Queueを使う

概要

競技プログラミング的なものを解いているときに, 優先度付きキューを使いたくなることがあるのでそのメモ

コード

.NETの標準ライブラリには存在しないので F#向けのコレクションライブラリを nugetから取ってくる. fsxで書いている場合は以下のような行を足す. projectがある場合は dotnet add で依存関係を追加する

#r "nuget: FSharpx.Collections"

定義を見るとわかるが, キューの要素は comparison制約を満たす必要があり, System.IComparableを実装している必要がある. IComparableを実装する際は Equals, GetHashCodeも overrideする必要があるのでそれを行う.

以下に例を示す. 年齢順, 年齢が同じなら名前で並べるというもの. F#で独自の比較, 等価性のチェックを行う場合は該当の methodを override, 実装するだけでなく属性も指定する必要があるようである.

open FSharpx.Collections

[<CustomEquality; CustomComparison>]
type PersonData =
    { Name: string
      Age: int }

    override this.GetHashCode() = hash this

    override this.Equals other =
        match other with
        | :? PersonData as o -> this.Name = o.Name && this.Age = o.Age
        | _ -> false

    interface System.IComparable with
        member this.CompareTo other =
            match other with
            | :? PersonData as o ->
                if this.Age <> o.Age then
                    compare this.Age o.Age
                else
                    compare this.Name o.Name
            | _ -> failwith "cannot compare with different object"

全体の例は以下の通り.

#r "nuget: FSharpx.Collections"

open FSharpx.Collections

[<CustomEquality; CustomComparison>]
type PersonData =
    { Name: string
      Age: int }

    override this.GetHashCode() = hash this

    override this.Equals other =
        match other with
        | :? PersonData as o -> this.Name = o.Name && this.Age = o.Age
        | _ -> false

    interface System.IComparable with
        member this.CompareTo other =
            match other with
            | :? PersonData as o ->
                if this.Age <> o.Age then
                    compare this.Age o.Age
                else
                    compare this.Name o.Name
            | _ -> failwith "cannot compare with different object"

let q =
    PriorityQueue.empty true
    |> PriorityQueue.insert { Name = "Alice"; Age = 42 }
    |> PriorityQueue.insert { Name = "Bob"; Age = 50 }
    |> PriorityQueue.insert { Name = "Chris"; Age = 18 }
    |> PriorityQueue.insert { Name = "David"; Age = 73 }
    |> PriorityQueue.insert { Name = "Elly"; Age = 33 }
    |> PriorityQueue.insert { Name = "Yoshida"; Age = 73 }

let (a, q1) = PriorityQueue.pop q
printfn "poped=%A" a // Yoshida

let (b, q2) = PriorityQueue.pop q1
printfn "poped=%A" b // David

let (c, q3) = PriorityQueue.pop q2
printfn "poped=%A" c // Bob

let (d, q4) = PriorityQueue.pop q3
printfn "poped=%A" d // Alice

let (e, q5) = PriorityQueue.pop q4
printfn "poped=%A" e // Elly

let (f, _) = PriorityQueue.pop q5
printfn "poped=%A" f // Chris

quadmath Perlでハマったところ備忘録

細々とメンテナンスしている Perlモジュールで quadmathが有効な Perlで失敗しているケースがいくつか見られたので対応していた. その対応に関する記録.

usequadmathとは

https://perldoc.perl.org/Config#usequadmath

数値の保持に可能であれば 4倍精度浮動小数点型である __float128を利用するコンフィグオプション. 設定を有効にしてビルドするためには libquadmathがインストールしておく必要がある.

名前だけ聞くと浮動小数点だけ影響があるのかと思うのだけど Perlような言語では整数とか浮動小数点はあいまいなので整数にも影響が出ておりテストがこけていた.

ハマった点

整数の精度が変わる

https://github.com/msgpack/msgpack-perl/blob/10544440a0a47f9d4a1ab78f75d6fccfe17bdeab/t/05_preferred_int.t

大きい整数リテラルを文字列化したときの精度が異なっており予期せぬテスト結果となっていた

#!/usr/bin/env perl

print "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFF=", 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFF, "\n";
print "0x800000000000000000000000000000=", 0x800000000000000000000000000000, "\n";

上記のスクリプトを quadmathが有効な Perlとそうでない Perlで実行した結果は以下の通り異なるものとなる.

quadmathが有効

0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFF=8.30767497365572420564879412675215e+34
0x800000000000000000000000000000=6.64613997892457936451903530140172e+35

quadmathが無効

0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFF=8.30767497365572e+34
0x800000000000000000000000000000=6.64613997892458e+35

github.com

対応は管理の保守の問題などから quadmath Perlではテストを行わないことにした.

数値から文字列への変換が適切に行えない

https://github.com/gfx/Perl-Data-Util/blob/f8db84cf6fe5c526adfa76d047bfd715296477bf/t/18_is_value.t

の is_integerの浮動小数点数の場合は数値を文字列化してそれを解析して判定を行っているが, 変換に使われていた関数が snprintfだったため正しく変換ができていなかった. snprintf等の標準ライブラリの変換関数は __float128には対応しておらず, quadmath_snprintfを使わなくてはならない.

https://perldoc.perl.org/perlapi#my_snprintf

ifdefで分けることを考えたが Perlソースコードを眺めていたところ, my_snprintfが quadmathを有効にしてビルドした場合内部で quadmath_snprintfを呼び出すような実装になっていたのでそちらを呼ぶように切り替えた。

github.com

modifierと specifierはべた書きしてしまうとポータビリティがなくなるので, Perlで定義されているマクロを使っています.

おわりに

Perlみたいな動的型言語で標準で使われる浮動小数点数の精度を変えるというのは影響は多すぎると思った. あとドキュメントがロクに書かれていないのも辛かった.