Declaratieve Talen/Oplossing Dijkstra Alternatief (Haskell)

Uit Wina Examenwiki
Naar navigatie springen Naar zoeken springen

Alternatieve oplossing (geeft pad en afstand terug)

import List

data Afstand = Afst Int | Inf deriving Show

instance Eq Afstand where
	(Afst x) == (Afst y) = x == y
	Inf == _ = False
	_ == Inf = False
	
instance Ord Afstand where
	(Afst x) <= (Afst y) = x <= y
	Inf <= _ = False
	_ <= Inf = True
	
------------------------------------------------------------------------
	
dijkstra :: String -> String -> (Afstand, String)
dijkstra s e = 
	let
		allknopen = knopen
		init = (Afst 0, s, s) : [(Inf, kn, "") | kn <- allknopen, kn /= s]
		tabel = dijkstra2 e allknopen init
		--
		distance = getdistance e tabel
		path = findpath s e tabel
	in	
		(distance, path)
		
------------------------------------------------------------------------
--- Dijkstra algoritme: stel tabel op (Afstand, Punt, KortsteAfstandPunt)

dijkstra2 :: String -> [String] -> [(Afstand, String, String)] -> [(Afstand, String, String)]
dijkstra2 endknoop remknopen tabel =
	let
		klknoop@(_, k, _) = zoek_kleinste remknopen tabel
		newremknopen = delete k remknopen
		newtable = updatetabel newremknopen klknoop tabel
	in
		if (k == endknoop)
		then
			newtable
		else
			dijkstra2 endknoop newremknopen newtable
		
updatetabel :: [String] -> (Afstand, String, String) -> [(Afstand, String, String)] -> [(Afstand, String, String)]
updatetabel _ _ [] = []
updatetabel remknopen klknoop@(dmin, kmin, _) (currknoop@(dcurr, kcurr, _) : xs) =
	if (elem kcurr remknopen)
	then
		let
			newcurrknoop = getnewdist klknoop currknoop
		in
			newcurrknoop : updatetabel remknopen klknoop xs
	else
		currknoop : updatetabel remknopen klknoop xs
	
getnewdist :: (Afstand, String, String) -> (Afstand, String, String) -> (Afstand, String, String)
getnewdist (dmin, kmin, kminorig) orig@(dcurr, kcurr, kcurrorig) =
	let
		dnew = plusafstand dmin (boog kmin kcurr)
		--dnew = min dcurr drol
	in
		if dcurr < dnew
		then
			orig --(dcurr, kcurr, kcurrorig)
		else
			(dnew, kcurr, kmin)

plusafstand :: Afstand -> Afstand -> Afstand
plusafstand Inf _ = Inf
plusafstand _ Inf = Inf
plusafstand (Afst x) (Afst y) = Afst (x + y)

zoek_kleinste :: [String] -> [(Afstand, String, String)] -> (Afstand, String, String)
zoek_kleinste remknopen [curr@(_, k, _)] =
	if (elem k remknopen)
	then
		curr
	else
		(Inf, "Error", "")
zoek_kleinste remknopen (newcandid@(d0, k0, _) : xs) = 
	let
		smallest@(d1, k1, _) = zoek_kleinste remknopen xs
	in
		if (d0 < d1 && (elem k0 remknopen))
		then
			newcandid
		else
			smallest

------------------------------------------------------------------------
--- Pad bepalen

findpath :: String -> String -> [(Afstand, String, String)] -> String
findpath s e table 	| s == e = s
					| otherwise = 
						let
							nextkey = getnext table e
						in
							(findpath s nextkey table) ++ e
						

getnext :: [(Afstand, String, String)] -> String -> String
getnext ((_, key0, nextkey) : xs) key1	| key0 == key1 = nextkey
										| otherwise = getnext xs key1

------------------------------------------------------------------------
--- Afstand opzoeken in tabel
getdistance :: String -> [(Afstand, String, String)] -> Afstand
getdistance key1 ((dist, key0, _) : xs)	| key0 == key1 = dist
										| otherwise = getdistance key1 xs
		

------------------------------------------------------------------------
-- Geimproviseerde gegevens

boog :: String -> String -> Afstand
boog s e	| dist s e < Inf	= dist s e
			| otherwise			= dist e s

dist :: String -> String -> Afstand
dist "a" "b" = Afst 1
dist "a" "c" = Afst 2
dist "a" "d" = Afst 10
dist "b" "c" = Afst 5
dist "c" "d" = Afst 1
dist _ _ = Inf

knopen :: [String]
knopen = ["a", "b", "c", "d"]