seq と LazyList

いつもの調子で思い付きで適当なこと言ってますな。後で見返してちょっとまずいと思ったのは「遅延シーケンス」について述べる時 Clojure を念頭に置いてしまっていること。Clojure では「遅延シーケンス」と「遅延リスト」が同じ物のように扱われます。さらにいうと永続的なコレクションさえ遅延シーケンス化されて処理されることもままあり、かなり特殊な仕様なんですよね。

●LazyList <'T>

すこし時間を遡って、

LazyList か...使ったことなかった。調べてみるとまさにこれが私のニーズだった。

一昨日のコードの関数をほんの少し変えただけ。受け取った seq を LazyList.ofSeq で遅延リストにして、あとは List モジュールを使う感覚です。とても簡単。素晴しい。問題となっていた処理速度も早い。
PowerPack に入っているのでコアアセンブリだけで使えないのが唯一の欠点かな。標準に入れるべきだ絶対。

●あえて seq で

seq を使うと何がいけないのか。実はまだ本当のところは理解できていないのですが、どうも seq は操作に気を付かわないと値の再計算が発生してしまうらしい。再計算させないためには「seq の要素を直接取り出さない」のが大事なようです。*1

再び @igetaさん

リンクを引用

// seealso: http://d.hatena.ne.jp/minazoko/20111204/1323007173
 
let distanceOfSameValueElements xs =
    Seq.scan (fun (i,dict) x -> match Map.tryFind x dict with
                                | None   -> i + 1, Map.add x i dict
                                | Some j -> i - j, Map.empty)
             (0, Map.empty)
             xs
    |> Seq.skip 1
    |> Seq.pick (fun (i,dict) -> if Map.isEmpty dict then Some i else None)

seq に対する逐次処理はこのようにシーケンス用の高階関数で要素を順に渡してもらうのがよさそうです。

*1:1回こっきりの処理ならべつにかまいませんが。ループが絡むとまずい。