我想爲Haskell實現一個最長路徑算法。我只使用了Haskell大約兩週的時間,並且之前沒有在功能語言中做過任何事情。如果您僅限於不可變數據和遞歸,嘗試在函數式語言中實現算法時,我確實迷失了方向。在Haskell中實現最長路徑算法
我一直在努力實現這個算法:http://www.geeksforgeeks.org/find-longest-path-directed-acyclic-graph/
My圖表構造是這樣的:
data = Graph w = Graph {vertices :: [(Char, w)],
edges :: [(Char, Char, w)]} deriving Show
所以我有兩個頂點和邊權重,權重可以是任何數據類型。因此,在計算最長路徑時,我還需要執行兩個函數,即f
和g
。然後,從頂點a
到b
的最長路徑將是路徑中所有權重的f(w)
和g(w)
之和。
我已經嘗試實現這個,但我總是發現自己試圖代碼的「勢在必行」的方式,它變得非常醜陋真快......
請點我在正確的方向。
weight_of_longest_path :: (Ord w) => Graph w -> Char -> Char
-> (w -> w) -> (w -> w) -> w
weight_of_longest_path (Graph v w) startVert endVert f g =
let
topSort = dropWhile (/= startVert) $ topological_ordering (Graph v w)
distList = zip topSort $
(snd $ head $ filter (\(a,b) -> a == startVert) v)
: (repeat (-999999999))
finalList = getFinalList (Graph v w) topSort distList f g
in
snd $ head $ filter (\(a,b) -> b == endVert) finalList
getFinalList :: (Ord w) => Graph w -> [Char] -> [(Char, w)]
-> (w -> w) -> (w -> w) -> [(Char, w)]
getFinalList _ [] finalList _ _ = finalList
getFinalList (Graph v w) (firstVert:rest) distList f g =
let
neighbours = secondNodes $ filter (\(a,b,w) -> a == firstVert) w
finalList = updateList firstVert neighbours distList (Graph v w) f g
in
getFinalList (Graph v w) rest finalList f g
updateList :: (Ord w) => Char -> [Char] -> [(Char, w)] -> Graph w
-> (w -> w) -> (w -> w) -> [(Char, w)]
updateList _ [] updatedList _ _ _ = updatedList
updateList firstVert (neighbour:rest) distList (Graph vertices weights) f g =
let
edgeWeight = selectThird $ head
$ filter (\(a,b,w) -> a == firstVert && b == neighbour) weights
verticeWeight = snd $ head
$ filter (\(a,b) -> a == neighbour) vertices
newDist = calcDist firstVert neighbour verticeWeight edgeWeight
distList f g
updatedList = replace distList neighbour newDist
in
updateList firstVert rest updatedList (Graph vertices weights) f g
calcDist :: (Ord w) => Char -> Char -> w -> w -> [(Char, w)]
-> (w -> w) -> (w -> w) -> w
calcDist firstVert neighbour verticeWeight edgeWeight distList f g =
if (compareTo f g
(snd $ head $ filter (\(a,b) -> a == neighbour) distList)
(snd $ head $ filter (\(a,b) -> a == firstVert) distList)
edgeWeight verticeWeight) == True
then
(f (snd $ head $ filter (\(a,b) -> a == firstVert) distList))
+ (g edgeWeight) + (f verticeWeight)
else
(f (snd $ head $ filter (\(a,b) -> a == neighbour) distList))
replace :: [(Char, w)] -> Char -> w -> [(Char, w)]
replace distList vertice value =
map (\[email protected](f, _) -> if f == vertice then (vertice, value) else p)
distList
正如你可以看到它是這樣一個簡單的算法很多亂七八糟的代碼,我敢肯定,它在一個更清潔的方式是可行的。
好吧,如果你使用'Data.Map'而不是你的頂點和邊的列表,那麼這段代碼可以被簡化(並且變得更有效率),這就是你的意思。 – Cubic
記事本+ ++爲您提到的geeks網站上的C++實現(不包括'main()')計算2484個字符,併爲您的1697個字符計算:) –
是的,但感覺就像我沒有使用函數式編程的力量,將distList從函數發送到函數並嘗試更新它...而某些函數只是以某種方式感覺像迭代器,這有點糟糕。 – hboy