F# でコマンドレットを書いてる pt.40
krymtkts/pocof の開発をした。
はじめメモリ浪費に気付いたときの使い方は pocof の起動までにめちゃくちゃ時間がかかる。
function global:Show-ReadLineHistory() {
Get-Content -Path (Get-PSReadLineOption).HistorySavePath | Select-Object -Unique | Select-Pocof -CaseSensitive -Layout TopDown
}
Set-Alias pghy Show-ReadLineHistory -Option ReadOnly -Force -Scope Global
こういう使い方をしてるのだけど、 Select-Object -Unique
がめちゃくちゃ遅い。
どういう実装をしてるかまでおってないが、順不同の要素中の他の重複を取り除くから遅いっぽい。
ここを Get-Unique
にしたら爆発的に速くなるのだけど、まず Sort-Object
で並び替えておく必要があり、それは期待した動作じゃない。元の順そのままで重複だけなくしたい。
そこまできたらもう面倒なので pocof に -Unique
オプションつけた方が楽やろなということで、そういう機能を実装した。 #181
PowerShell で言うところのこういうコードにしたら、とても速く重複が取り除ける。
Get-Content -Path (Get-PSReadLineOption).HistorySavePath | % -begin {$a=[ordered]@{}} -process {if ($a.Contains($_)) {} else {$a.Add($_, $null)}} -end {$a.Keys}
[ordered]
は pocof のコードだと System.Collections.Specialized.OrderedDictionary
になる。
pocof のInputObject
の並び順を維持しつつとなるとこいつを使うのが定石だろう。
System.Collections.Specialized.OrderedDictionary
では key の等価性を判定するのに Equals
と GetHashCode
が使われる。
なので PSCustomObject
の場合だと重複が取り除けない。 PSCustomObject
は Object.Equals
の再定義とかしてないから参照等価性しかチェックしてないみたい。
ちょっと残念だが、でも PowerShell の環境では大体の object はそれらを実装してるし、いったん PSCustomObject
用の特別な比較関数を書かないでやった。
-Unique
オプション対応してみてわかったのだけど、 hashtable
の key-value の組み合わせが一致するエントリも除外できるし、こりゃなかなか便利な機能な気がしている。ぱぱっと思いついて作った割には。
pocof の設計思想的に、 pocof の呼び出し前後で他の Cmdlet でできることはあまり実装しないことにしてる。
それが PowerShell らしいかなと(勝手に)と考えて -Unique
オプションは考えてこなかった。
でも今回の件は学びになった。
既存の Cmdlet で出来たとしても、長々とパイプラインを書かないといけないとかで複雑になったり、パフォーマンスが振るわないようであれば、機能に取り込んでも良さそうやなという感覚。
この -Unique
は早速 0.13.0 でリリースして、自分の PowerShell profile で新しいオプション使うように直した。