user5402の答えはそれを行うための非常にきれいな方法ですが、Data.Vector
ドキュメントに記載されている効率の問題の犠牲になります。具体的には、挿入スポットを見つけてやみくもにコピーすると、ソースベクターから実際に値を読み取ることを強制しなくなります。代わりに、強制的にソース ベクターから読み取ったサンクでデスティネーション ベクターを埋めます。
オリジナルソリューション
注:これは私が思いついた最初の解決策です。理解するのは非常に簡単ですが、 のストリーム フュージョン システムではうまく機能せず、vector
パフォーマンスが比較的低下する可能性があります。以下の解決策は、一般的に優れています。
ドキュメントで説明されているように、1 つの解決策は、モナドindexM
演算を使用してこれらのブラインド読み取りを実行することです。これにより、読み取りが強制的に実行されますが、読み取り値は強制されません。したがって、ポインター (場合によってはサンクへのポインター) をソース ベクターから宛先ベクターにコピーします。unsafe
最大限の効率を得るには、以下のすべてをそのバリアント ( unsafeIndexM
、unsafeIndex
、およびunsafeWrite
特に)に置き換える必要があります。
{-# Language ScopedTypeVariables #-}
module Insert where
import qualified Data.Vector as V
import Data.Vector (Vector)
import qualified Data.Vector.Mutable as MV
import Data.Vector.Mutable (MVector)
import Control.Monad.ST
insertElem :: forall a . Ord a => a -> Vector a -> Vector a
insertElem e v = V.create act
where
act :: forall s . ST s (MVector s a)
act = do
mv <- MV.new (V.length v + 1)
let
start :: Int -> ST s (MVector s a)
start i
| i == V.length v ||
e <= v V.! i = MV.write mv i e >> finish i
| otherwise = MV.write mv i (v V.! i) >> start (i+1)
finish :: Int -> ST s (MVector s a)
finish i
| i == V.length v = return mv
| otherwise = do
V.indexM v i >>= MV.write mv (i+1)
finish (i+1)
in start 0
insertElemInt :: Int -> Vector Int -> Vector Int
insertElemInt = insertElem
act
アクションに名前を付けて使用するScopedTypeVariables
必要は実際にはありませんが、間違いを追跡するのに非常に役立つことがわかりました.
ストリームベースのソリューション
上記のコードは、ストリーム フュージョンではうまく機能しません。インデックスがあちこちに飛んでいるためです。次のアプローチは適切に融合し、不要なサンクの作成を回避する必要があります。ストリームフュージョンコードに触れるのはこれが初めてなので、改善できる点があるかもしれません。この最初のストリーム ベース バージョンの唯一の問題は、融合した場合、入力ストリームのステップ関数が要素の 1 つで 2 回実行されることです。これは通常は問題になりませんが、ステップ関数が非常に高価な (まれな) 場合は、問題になる可能性があります。したがって、その場合により適切に機能する代替手段を提供します。これらのどちらが実際にいつより優れているかはわかりませんので、両方を含めます。
これらのストリームベースのソリューションのいずれかを使用すると、コードは
testEnum :: Word -> Word -> Word -> Word
testEnum ins low high = V.product $
insertElem ins $ V.enumFromStepN low 1 (fromIntegral high)
定数空間で実行されるループにコンパイルされ、実際にはベクターはまったく作成されません。
1つのシードを2回ステップ
{-# Language ScopedTypeVariables #-}
module Insert where
import Data.Vector (Vector)
import Data.Word (Word)
import qualified Data.Vector.Fusion.Stream.Monadic as S
import qualified Data.Vector.Generic as G
import Data.Vector.Fusion.Util (Id(..))
-- To check on unboxing and such
insertElemWord :: Word -> Vector Word -> Vector Word
insertElemWord = insertElem
{-# INLINE insertElem #-}
insertElem :: forall a . Ord a => a -> Vector a -> Vector a
insertElem a v = G.unstream (insertElemS a (G.stream v))
{-# INLINE [1] insertElemS #-}
insertElemS :: forall a . Ord a => a -> S.Stream Id a -> S.Stream Id a
insertElemS e (S.Stream step (state::s) size) = S.Stream step' (state, False) (size + 1)
where
{-# INLINE [0] step' #-}
step' :: (s, Bool) -> Id (S.Step (s, Bool) a)
step' (s, True) = Id $ case unId (step s) of
S.Yield a s' -> S.Yield a (s', True)
S.Skip s' -> S.Skip (s', True)
S.Done -> S.Done
step' (s, False) = Id $ case unId (step s) of
S.Yield a s' ->
if e <= a
then S.Yield e (s, True)
else S.Yield a (s', False)
S.Skip s' -> S.Skip (s', False)
S.Done -> S.Yield e (s, True)
ルックアップ/ステップを繰り返さない、正確に 1 つのパス
{-# Language ScopedTypeVariables #-}
module Insert where
import Data.Vector (Vector)
import Data.Word (Word)
import qualified Data.Vector.Fusion.Stream.Monadic as S
import qualified Data.Vector.Generic as G
import Data.Vector.Fusion.Util (Id(..))
data Status s a = Pre s | During s a | Post s | End
insertElemWord :: Word -> Vector Word -> Vector Word
insertElemWord = insertElem
{-# INLINE insertElem #-}
insertElem :: forall a . Ord a => a -> Vector a -> Vector a
insertElem a v = G.unstream (insertElemS a (G.stream v))
{-# INLINE [1] insertElemS #-}
insertElemS :: forall a . Ord a => a -> S.Stream Id a -> S.Stream Id a
insertElemS e (S.Stream step (state::s) size) = S.Stream step' (Pre state) (size+1)
where
{-# INLINE [0] step' #-}
step' :: Status s a -> Id (S.Step (Status s a) a)
step' (Post s) = Id $ case unId (step s) of
S.Yield a s' -> S.Yield a (Post s')
S.Skip s' -> S.Skip (Post s')
S.Done -> S.Done
step' (Pre s) = Id $ case unId (step s) of
S.Yield a s'
| e <= a -> S.Yield e (During s' a)
| otherwise -> S.Yield a (Pre s')
S.Skip s' -> S.Skip (Pre s')
S.Done -> S.Yield e End
step' (During s a) = Id (S.Yield a (Post s))
step' End = Id S.Done