楽しみのために、単純な最長パス アルゴリズムの実装を作成しようとしています (循環グラフで最長の非循環パスの長さを見つけるため)。私は命令型アルゴリズムを直接移植することから始めました。これは機能し、かなりうまく機能しました。
data Route = Route {dest:: !Int32, cost:: !Int32}
type Node = [Route]
lPathImperative :: V.Vector Node -> Int32 -> UMV.IOVector Bool -> IO (Int32)
lPathImperative !nodes !nodeID !visited = do
UMV.write visited (fromIntegral nodeID) True
max <- newIORef 0
Prelude.mapM_ (\ Route{dest, cost} -> do
isVisited <- UMV.read visited (fromIntegral dest)
case isVisited of
True -> return ()
False -> do
dist <- fmap (+ cost) $ lPathImperative nodes dest visited
maxVal <- readIORef max
if dist > maxVal then writeIORef max dist else return ())
(nodes V.! (fromIntegral nodeID))
UMV.write visited (fromIntegral nodeID) False
readIORef max
は、グラフ内の各ノードが現在アクセスされているかどうか、すべてが false に初期化されているかどうか、およびnodesvisited
はノードのベクトルであるかどうかを表す、ボックス化されていない bool の可変ベクトルです。
max
次に、次のように、IORef としてではなく、フォールドで渡される値として持つことで、より機能的にしようとしました。
lPathFun :: V.Vector Node -> Int32 -> UMV.IOVector Bool -> IO (Int32)
lPathFun !nodes !nodeID !visited = do
UMV.write visited (fromIntegral nodeID) True
let max = CM.foldM acc (0::Int32) (nodes V.! (fromIntegral nodeID))
UMV.write visited (fromIntegral nodeID) False
max
where
acc :: Int32 -> Route -> IO (Int32)
acc maxDist Route{dest,cost} = do
isVisited <- UMV.read visited (fromIntegral dest)
case isVisited of
True -> return maxDist
False -> do
dist <- fmap (+ cost) $ lPathFun nodes dest visited
return $ if dist > maxDist then dist else maxDist
ただし、このバージョンは完了するのに失敗し、数分間実行されます (同じ入力に数秒かかりました) out of memory (requested 1048576 bytes)
。lPathFun
誰かが私のコードを見て、私が間違っていることを確認できれば幸いです。私はその中のすべてを厳密にしようとしましたが、それは役に立ちませんでした.また、すべてを怠惰にしようとしましたが、変更はありません. 代わりにstrictに変更type node
して使用しようとしましたが、役に立ちませんでした。V.Vector route
foldM'
問題はスペースリークだと思います。これは、私がlPathFun
OCaml に翻訳しようとしたところ、問題なく動作したためです (OCaml バージョンが手動再帰を使用しているという事実は違いを生むべきではありません: 私の機能的な Haskell バージョンも最初は手動再帰を使用していましたが、foldM を使用した場合と同じ問題に悩まされていました):
type route = {dest: int; cost: int}
type node = route array
let rec lPathFun (nodes: node array) nodeID visited =
visited.(nodeID) <- true;
let rec loop i maxDist =
if i < 0 then maxDist
else
let neighbour = nodes.(nodeID).(i) in
if (not visited.(neighbour.dest))
then
let dist = neighbour.cost + lPathFun nodes neighbour.dest visited in
let newMax = if dist > maxDist then dist else maxDist in
loop (i-1) newMax
else
loop (i-1) maxDist in
let (max: int) = loop (Array.length nodes.(nodeID) - 1) 0 in
visited.(nodeID) <- false;
max;;
私が使用している GHC のバージョンは 7.8.3 です。