Download presentation
Presentation is loading. Please wait.
Published byGloria Hancock Modified over 8 years ago
1
Case Study: Route Finding Lecture 6, Programmeringsteknik del A.
2
The Problem GöteborgBorås Jönköping ÖrebroStockholm 7189 285 211 294 205335 Which is the shortest route to Stockholm?
3
The Program > route ”Göteborg” ”Stockholm” 71 Borås 160 Jönköping 324 Norrköping 495 Stockholm
4
The Map Data Keep the map data in a file, so it is easy to modify. GöteborgBorås71 BoråsJönköping89 JönköpingNorrköping164 NorrköpingÖrebro105 NorrköpingUppsala243 NorrköpingVästerås153 Norrköping Stockholm171... roads
5
Overall Plan We begin with a top-down design: Read the names of the starting point and end point. Read the roads data. Find the shortest route. Display the result.
6
Main Program main :: IO () main = do(from, to) <- readArguments roads <- readRoads putStr (formatRoute (route roads from to)) What are the types here? readArguments :: IO (Town, Town) readRoads :: IO [Road] route :: [Road] -> Town -> Town -> Route formatRoute :: Route -> String The ”main program” is always a command called main.
7
Top-Down Design We have broken the problem down into parts, and written the top-level program. We go on to solve each part. When we put the solutions together, we obtain a complete program.
8
Reading the Arguments Haskell programs communicate with UNIX using a standard library called System. In this library we find getArgs :: IO [String] To use it, we must add import System at the top of our program. A list of the arguments on the command line.
9
readArguments Let us represent towns by strings: type Town = String readArguments :: IO (Town, Town) readArguments = do[from,to] <- getArgs return (from, to)
10
The Road Data The roads file contains: GöteborgBorås71 BoråsJönköping89 JönköpingNorrköping164 NorrköpingÖrebro105... In our program, we represent this information in a form which is convenient to manipulate: [(”Göteborg”, ”Borås”, 71), (”Borås”, ”Jönköping”, 89), …]
11
The Road Type We make an appropriate type definition: type Road = (Town, Town, Int) Now we must define readRoads :: IO [Road]
12
Reading the Roads Data Break the problem into simpler stages: GöteborgBorås71 BoråsJönköping89... [”GöteborgBorås71”, ”BoråsJönköping89”, …] Break the input into a list of lines.
13
Reading the Roads Data Ctd Break the problem into simpler stages: [”GöteborgBorås71”, ”BoråsJönköping89”, …] Break each line into a list of ”words”. [[”Göteborg”, ”Borås”, ”71”], [”Borås”, ”Jönköping”, ”89”], …]
14
Reading the Roads Data Ctd Break the problem into simpler stages: Turn each list of words into a road. [[” Göteborg”, ”Borås”, ”71”], [”Borås”, ”Jönköping”, ”89”], …] [(” Göteborg”, ”Borås”, 71), (”Borås”, ”Jönköping”, 89), …]
15
Breaking a String into Lines ”a\nb\nc”[”a”, ”b”, ”c”] We must go through the list -- but a list comprehension won’t help. We could try recursion -- but it still isn’t easy. Can we solve a simpler problem?
16
Taking One Line from a String ”a\nb\nc””a” \n...ba ”” a...\nbab takeLine :: String -> String takeLine (’\n’:xs) = ”” takeLine (x : xs) = x : takeLine xs takeLine ”” = ””
17
Dropping One Line from a String ”a\nb\nc””b\nc” \n...ba a \nb dropLine :: String -> String dropLine (’\n’:xs) = xs dropLine (x : xs) = dropLine xs dropLine ”” = ””...ba
18
Splitting a String into Lines ”ab\ncd\nef” ”ab” ”cd\nef” [”cd”, ”ef”] [”ab”, ”cd”, ”ef”] takeLinedropLine lines :
19
Defining lines lines :: String -> [String] lines [] = [] lines xs = takeLine xs : lines (dropLine xs) This is actually a standard function…!
20
Defining readRoads Reminder:type Road = (Town, Town, Int) readRoads :: IO [Road] readRoads = do xs <- readFile ”roads” return [road (words line) | line <- lines xs] road :: [String] -> Road road [from, to, dist] = (from, to, read dist) For each line Split into words (very like lines) Convert to a road Convert a String to e.g. a number.
21
Reminder: The Main Program main :: IO () main = do(from, to) <- readArguments roads <- readRoads putStr (formatRoute (route roads from to)) What remains to define? route :: [Road] -> Town -> Town -> Route formatRoute :: Route -> String
22
Representing a Route What do we need to know about a route? Where it leads from. Where it leads to. How long it is (so we can choose the shorter of two alternatives). Which towns it leads through (so we can print it out). 71 Borås 160 Jönköping 324 Norrköping 495 Stockholm
23
Representing a Route: First Try 71 Borås 160 Jönköping 324 Norrköping 495 Stockholm type Route = (Town, Town, Int, [(Town, Int)]) example = (”Göteborg”, ”Stockholm”, 495, [(”Borås”, 71), (”Jönköping”, 160), (”Norrköping”, 324), (”Stockholm”, 495)])
24
Formatting a Route Once again, solve the problem in stages. formatRoute :: Route -> String formatRoute (from, to, dist, stages) = unlines [formatStage s | s <- stages] Join up the lines with line breaks. Format one stage of the route.
25
Formatting a Stage formatStage :: (String, Int) -> String First try:formatStage (t, d) = show d ++ ” ” ++ t Output: 71 Borås 160 Jönköping 324 Norrköping 495 Stockholm
26
Padding with Spaces Second try: formatStage (t, d) = pad 5 (show d) ++ ” ” ++ t pad :: Int -> String -> String pad n s = [’ ’ | i<-[length s+1..n]] ++ s As many spaces as are needed to fill the gap between length s and n.
27
Joining Up Lines ”ab””ef””cd” ”cd\nef\n” ”ab\ncd\nef\n” unlines :: [String] -> String unlines (x : xs) = x ++ ”\n” ++ unlines xs unlines [] = [] Another standard function.
28
Remaining Problem All that remains to do is to define route :: [Road] -> Town -> Town -> Route This is the hard part!
29
How Can We Get from Göteborg to Stockholm? GöteborgBorås Jönköping ÖrebroStockholm 7189 285 211 294 205335 Find the best routes from Örebro and Borås, add the distance from Göteborg, and choose the best.
30
Choosing the Better of Two Routes Which choice is better? A shorter route is better than a longer one. If two routes have the same length, then the one with more stages is better (because it gives a more detailed route description). (”Goteborg”, ”Stockholm”, 496, [(”Orebro”,285), (”Stockholm”,496)]) is better than (”Goteborg”, ”Stockholm”, 496, [(”Stockholm”, 496)])
31
better better :: Route -> Route -> Route better (from,to,dist,stgs) (from',to',dist',stgs') | dist < dist' = (from,to,dist,stgs) | dist > dist' = (from',to',dist',stgs') | length stgs > length stgs' = (from,to,dist,stgs) | length stgs <= length stgs' = (from',to',dist',stgs') best :: [Route] -> Route best [r] = r best (r : rs) = better r (best rs) Choose the best of many routes.
32
Joining Routes Together We will need to combine a one-stage route (e.g. Goteborg to Boras) with the route onwards. How can we join two routes? The first route must end where the second begins. We add the distances. We must adjust the stages in the second route, by adding the length of the first route to their distances.
33
Example of Joining Routes Joining (”Goteborg”, ”Boras”, 71, [(”Boras”, 71)]) with (”Boras”, ”Jonkoping”, 89, [(”Jonkoping”, 89)]) should give (”Goteborg”, ”Jonkoping”, 160, [(”Boras”, 71), (”Jonkoping”, 160)]) First stage unchanged. Second stage with adjusted distance.
34
joinRoutes joinRoutes :: Route -> Route -> Route joinRoutes (from,to,dist,stgs) (from',to',dist',stgs') | to==from' = (from,to',dist+dist', stgs++[(c,d+dist) | (c,d)<-stgs']) For each stage in the second route... Adjust the distance.
35
Making a One Stage Route We will need to convert a road into a one-stage route, so that we can use joinRoute on it. Recalltype Road = (Town, Town, Int) roadRoute :: Road -> Route roadRoute (from,to,dist) = (from,to,dist,[(to,dist)]) Example: roadRoute (”Goteborg”, ”Boras”, 71) (”Goteborg”, ”Boras”, 71, [(”Boras”, 71)])
36
Roads Go Both Ways To keep the route finder simple, for every road in the roads data, we add a road in the opposite direction. bothWays :: [Road] -> [Road] bothWays roads = roads ++ [(to,from,dist) | (from,to,dist) <- roads]
37
Programming the Route Finder route :: [Road] -> City -> City -> Route route roads from to = best [joinRoutes (roadRoute road) (route roads (endPoint road) to) | road <- bothWays roads, startPoint road == from] startPoint, endPoint :: Road -> Town startPoint (from, to, dist) = from endPoint (from, to, dist) = to Useful auxiliary functions.
38
The Base Case When we are already in Stockholm, the route there is trivial! Add before the recursive case: route roads from to | from == to = trivialRoute from We must be able to create a trivial route: trivialRoute :: Town -> Route trivialRoute t = (t, t, 0, [])
39
Testing the Program > runhugs Route.hs Goteborg Stockholm ERROR: Control stack overflow. Every time hugs calls a function, it must record what it was doing beforehand, so as to be able to continue when the function call is complete. Imagine that hugs writes a little note. These notes are piled up on the `control stack’. Now the stack is overfull -- because we have a recursion without end.
40
Understanding the Recursion GöteborgBorås Jönköping ÖrebroStockholm 7189 285 211 294 205335 What is getting smaller? Which are the best routes from Boras to Stockholm?
41
Avoiding Circles GöteborgBorås Jönköping ÖrebroStockholm 7189 285 211 294 205335 Find the best route from Boras to Stockholm avoiding Goteborg. Find the best route from Jonkoping to Stockholm avoiding Goteborg and Boras.
42
Finding Routes Avoiding Towns Define routeAvoiding :: [Town] -> [Road] -> Town -> Town -> Route Avoiding these towns. Then route roads from to = routeAvoiding [] roads from to
43
routeAvoiding routeAvoiding avoid roads from to | from == to = trivialRoute from | otherwise = best [joinRoutes (roadRoute road) (routeAvoiding (from : avoid) roads (endPoint road) to) | road <- bothWays roads, startPoint road == from, not (endPoint road `elem` avoid)] Avoid returning to from. Add another condition: do not follow a road to a town to be avoided.
44
A New Problem > runhugs Route.hs Goteborg Stockholm Program error: {best []} It is possible that every road from a town leads to a town we should avoid! Then no route can be found!
45
One Solution: Changing the Route Representation We need to be able to represent an invalid route. Redefine type Route = (Bool, … as before …) invalidRoute :: Route invalidRoute = (False, ””, ””, 0, []) True if the route is valid.
46
Adjust The Functions on Routes formatRoute (False, _, _, _, _) = ”No valid route” formatRoute (True, …) = …as before… better (True, …) (True, …) = …as before… better (False, _, _, _, _) r = r better r (False, _, _, _, _) = r best [] = invalidRouteand so on... Always prefer a valid route.
47
At Last, It Works! > runhugs Route.hs Goteborg Stockholm 71 Borås 160 Jönköping 324 Norrköping 495 Stockholm
48
Refinements The program works, but it is slow. As we add more roads, it becomes much much slower. Goteborg Orebro Stockholm Jonkoping It is much better to work out all the shortest routes, between every pair of towns, at the same time!
49
Lessons Top-down design enables us to solve difficult problems in many stages. Lists are a natural way to represent much real world data. Recursion is a powerful tool for solving difficult problems. ’Obvious’ solutions can have subtle errors. Algorithm design is a subtle art: there is a lot to learn!
Similar presentations
© 2024 SlidePlayer.com. Inc.
All rights reserved.