对于我的学习,我必须编写以下函数,它可以获取两国之间最短的路线。我已经编写了一个函数 isRoute 来检查两个国家之间是否存在连接,以及一个函数 yieldRoute 只返回两个国家之间的连接。现在我必须编写一个函数来返回两国之间的最短路线。
我的第一种方法是获取两国之间的所有连接,然后获取最短的一个,但在我看来,获取所有连接对编程来说有点烦人。现在我想出了实现 dijstra 算法的想法,但我实际上也觉得这有点难。你们能给我一些想法如何做到这一点吗?
我们必须使用这些类型(我们不允许更改它们,但我们可以添加新类型。)
type Country = String
type Countries = [Country]
type TravelTime = Integer -- Travel time in minutes
data Connection = Air Country Country TravelTime
| Sea Country Country TravelTime
| Rail Country Country TravelTime
| Road Country Country TravelTime deriving (Eq,Ord,Show)
type Connections = [Connection]
data Itinerary = NoRoute | Route (Connections,TravelTime) deriving (Eq,Ord,Show)
我的收益路线功能,它只是广度优先搜索:(德语评论很抱歉)
-- Liefert eine Route falls es eine gibt
yieldRoute :: Connections -> Country -> Country -> Connections
yieldRoute cons start goal
| isRoute cons start goal == False = []
| otherwise = getRoute cons start [] [start] goal
getRoute :: Connections -> Country -> Connections -> Countries -> Country -> Connections
getRoute cons c gone visited target
| (c == target) = gone
| otherwise = if ( visit cons c visited ) then ( getRoute cons (deeper cons c visited) (gone ++ get_conn cons c (deeper cons c visited)) (visited ++ [(deeper cons c visited)]) target ) else ( getRoute cons (back (drop (length gone -1) gone)) (take (length gone -1) gone) visited target )
-- Geht ein Land zurück
back :: Connections -> Country
back ((Air c1 c2 _):xs) = c1
back ((Sea c1 c2 _):xs) = c1
back ((Rail c1 c2 _):xs) = c1
back ((Road c1 c2 _):xs) = c1
-- Liefert das nächste erreichbare Country
deeper :: Connections -> Country -> Countries -> Country
deeper ((Air c1 c2 _):xs) c visited
| (c1 == c) = if ( c2 `elem` visited ) then ( deeper xs c visited ) else c2
| (c2 == c) = if ( c1 `elem` visited ) then ( deeper xs c visited ) else c1
| otherwise = deeper xs c visited
deeper ((Sea c1 c2 _):xs) c visited
| (c1 == c) = if ( c2 `elem` visited ) then ( deeper xs c visited ) else c2
| (c2 == c) = if ( c1 `elem` visited ) then ( deeper xs c visited ) else c1
| otherwise = deeper xs c visited
deeper ((Rail c1 c2 _):xs) c visited
| (c1 == c) = if ( c2 `elem` visited ) then ( deeper xs c visited ) else c2
| (c2 == c) = if ( c1 `elem` visited ) then ( deeper xs c visited ) else c1
| otherwise = deeper xs c visited
deeper ((Road c1 c2 _):xs) c visited
| (c1 == c) = if ( c2 `elem` visited ) then ( deeper xs c visited ) else c2
| (c2 == c) = if ( c1 `elem` visited ) then ( deeper xs c visited ) else c1
| otherwise = deeper xs c visited
-- Liefert eine Connection zwischen zwei Countries
get_conn :: Connections -> Country -> Country -> Connections
get_conn [] _ _ = error "Something went terribly wrong"
get_conn ((Air c1 c2 t):xs) c3 c4
| (c1 == c3) && (c2 == c4) = [(Air c1 c2 t)]
| (c1 == c4) && (c2 == c3) = [(Air c1 c2 t)]
| otherwise = get_conn xs c3 c4
get_conn ((Sea c1 c2 t):xs) c3 c4
| (c1 == c3) && (c2 == c4) = [(Air c1 c2 t)]
| (c1 == c4) && (c2 == c3) = [(Air c1 c2 t)]
| otherwise = get_conn xs c3 c4
get_conn ((Road c1 c2 t):xs) c3 c4
| (c1 == c3) && (c2 == c4) = [(Air c1 c2 t)]
| (c1 == c4) && (c2 == c3) = [(Air c1 c2 t)]
| otherwise = get_conn xs c3 c4
get_conn ((Rail c1 c2 t):xs) c3 c4
| (c1 == c3) && (c2 == c4) = [(Air c1 c2 t)]
| (c1 == c4) && (c2 == c3) = [(Air c1 c2 t)]
| otherwise = get_conn xs c3 c4
-- Überprüft ob eine besuchbare Connection exestiert
visit :: Connections -> Country -> Countries -> Bool
visit [] _ _ = False
visit ((Air c1 c2 _):xs) c visited
| (c1 == c) = if ( c2 `elem` visited) then ( visit xs c visited ) else True
| (c2 == c) = if ( c1 `elem` visited) then ( visit xs c visited ) else True
| otherwise = visit xs c visited
visit ((Sea c1 c2 _):xs) c visited
| (c1 == c) = if ( c2 `elem` visited) then ( visit xs c visited ) else True
| (c2 == c) = if ( c1 `elem` visited) then ( visit xs c visited ) else True
| otherwise = visit xs c visited
visit ((Rail c1 c2 _):xs) c visited
| (c1 == c) = if ( c2 `elem` visited) then ( visit xs c visited ) else True
| (c2 == c) = if ( c1 `elem` visited) then ( visit xs c visited ) else True
| otherwise = visit xs c visited
visit ((Road c1 c2 _):xs) c visited
| (c1 == c) = if ( c2 `elem` visited) then ( visit xs c visited ) else True
| (c2 == c) = if ( c1 `elem` visited) then ( visit xs c visited ) else True
我现在必须写这个:
yieldFastestRoute :: Connections -> Country -> Country -> Itinerary
Dijkstra 算法: http ://en.wikipedia.org/wiki/Dijkstra%27s_algorithm
我的第一种方法是:(正如我所说的 getallRoutes 有问题)
yieldFastestRoute :: Connections -> Country -> Country -> Itinerary
yieldFastestRoute cons start targ
|(isRoute start targ == False) = NoRoute
|otherwise = (Route (getFastest (getAllRoutes cons start targ)) (sumTT (getFastest (getAllRoutes cons start targ))))
-- Liefert alle Routen zwischen zwei Ländern
getAllRoutes :: Connections -> Country -> Country -> [Connections]
-- Liefert aus einer Reihe von Connections die schnellste zurück
getFastest :: [Connections] -> Connections
getFastest (x:xs) = if ( (sumTT x) < sumTT (getFastest xs) || null (getFastest xs) ) then x else ( getFastest xs )
sumTT :: Connections -> TravelTime
sumTT [] = 0
sumTT ((Air _ _ t ): xs) = t ++ sumTT xs
sumTT ((Rail _ _ t ): xs) = t ++ sumTT xs
sumTT ((Road _ _ t ): xs) = t ++ sumTT xs
sumTT ((Sea _ _ t ): xs) = t ++ sumTT xs
我基本上想知道在 Haskell 中实现 Dijkstra 的最佳方法是什么,或者我是否可以遵循另一种方法。