jbappleの優秀な答えにリフに関してはreplicate
、しかし、使用してreplicateA
(そのreplicate
代わりに、私は次のよう思い付いた上に構築されています):
--Unlike fromList, one needs the length explicitly.
myFromList :: Int -> [b] -> Seq b
myFromList l xs = flip evalState xs $ Seq.replicateA l go
where go = do
(y:ys) <- get
put ys
return y
myFromList
(もう少し効率的なバージョン)は、ソートの結果であるフィンガーツリーを構築するために内部で既に定義され、使用されData.Sequence
ています。
一般的に、直感replicateA
は簡単です。applicativeTree関数のreplicateA
上に構築されます。大きさの木片を取りますapplicativeTree
m
n
このコピーを含むバランスの取れたツリーを生成します。用ケースn
8まで(シングルDeep
フィンガー)がハードコーディングされています。これより上にあるものはすべて、それ自体を再帰的に呼び出します。「適用可能な」要素とは、上記のコードの場合のように、ツリーの構築をスレッド効果でインターリーブすることです。
go
複製された機能は、単に現在の状態を取得する動作であり、上から要素をポップし、そして残りを置き換えます。したがって、呼び出しごとに、入力として提供されたリストをさらに下に進みます。
より具体的なメモ
main = print (length (show (Seq.fromList [1..10000000::Int])))
いくつかの簡単なテストで、これは興味深いパフォーマンスのトレードオフをもたらしました。上記のメイン関数は、myFromListを使用した場合よりも約1/3低く実行されましたfromList
。一方、myFromList
標準のながら、2メガバイトの一定のヒープを使用するfromList
926メガバイトまで使用。926MBは、リスト全体を一度にメモリに保持する必要があるために発生します。一方、このソリューションmyFromList
は、遅延ストリーミング形式で構造を使用できます。速度の問題myFromList
は、(状態モナドの構築/破棄のペアの結果として)約2倍の割り当てを実行する必要があるという事実に起因しますfromList
。CPS変換された状態モナドに移動することにより、これらの割り当てを削除できますが、遅延が発生するとリストを非ストリーミングでトラバースする必要があるため、常により多くのメモリを保持することになります。
一方、ショーでシーケンス全体を強制するのではなく、頭または最後の要素を抽出するだけに移り、myFromList
すぐに大きな勝利を提示する場合-頭の要素の抽出はほぼ瞬時であり、最後の要素の抽出は0.8秒です。一方、標準fromList
では、先頭要素または最後の要素のいずれかを抽出するには、約2.3秒かかります。
これはすべて詳細であり、純度と怠lazの結果です。突然変異とランダムアクセスのある状況でreplicate
は、ソリューションの方が厳密に優れていると思います。
しかし、それは書き換える方法があるかどうかの問題提起しapplicativeTree
、そのようなmyFromList
厳密に、より効率的であるが。問題は、適用アクションがツリーが自然にトラバースされる順序とは異なる順序で実行されることだと思いますが、これがどのように機能するか、またはこれを解決する方法があるかどうかについては完全に取り組んでいません。