pvl
pvl

Reputation: 27

How do I calculate the sum from my functions?

I have a file (points.txt) with some Cartesian coordinates defined:

A 1.0 2.2
B 2.1 3.0
C 3.5 4.0
D 4.0 5.0

I have a second file (routes.txt) with routes defined based on the points from points.txt.

route1 ACDB
route2 ABC

I need to find the length of each route. So far I have a calculation of the distance between two points, like this:

type Point = (String, Float, Float)

distance_points :: IO ()
distance_points = do s <- readFile "pontos.txt"
                      putStr "Fom: "
                      p1 <- getLine
                      putStr "To: "
                      p2 <- getLine
                      print ( distance (search_point p1 (map words (lines s))) (search_point p2 (map words (lines s))))

search_point :: String -> [[String]] -> Point
search_point pt ([p,c1,c2]:xs) = if pt == p then (p, read(c1)::Float, read(c2)::Float)
                                             else search_point pt xs

distance :: Point -> Point -> Float
distance (s1,x1,y1) (s2,x2,y2) = sqrt ((x1-x2)^2 + (y1-y2)^2)

How can I calculate the full distance of a route?

Also, if I have several routes how can I find the longest one?

Thanks in advance.

Upvotes: 2

Views: 237

Answers (1)

AndrewC
AndrewC

Reputation: 32455

Step 1: Separate pure and non-pure code, by not writing IO till the end.

The easiest way to do this is to solve your problem in pure code first, then add the file-reading afterwards. Otherwise you'll be tempted to write a lot of IO code.

It'll be easier if you separate the name from the coordinates:

type Coordinates = (Float,Float)
type Name = Char -- You had String, which is also fine
type Point = (Name, Coordinates)
type Points = [Point] -- or Map String Point from Data.Map

Then have some practice data:

sampleData :: Points
sampleData = [('A',(1.0,2.2), .....

Step 2: Write a function that takes a collection of points and a pair of point names and returns a distance

First you'll need a function that takes a name and gives you some coordinates.

coordinates :: Points -> Name -> Coordinates

If we're using [Point], the easiest way to do this is to use lookup. (You can find out about functions on hoogle like this or by type like this, although there's no obvious way for you to know that you wanted a Maybe, and when you just search for [(a,b)] -> b, lookup is a long way down.)

Comment if you need help with this step.

Using that you'll be able to write

distBetween :: Points -> Name -> Name -> Float

Step 3: Turn a list of names representing a path into a list of pairs of point names

getPath :: String -> [(Name,Name)]

or (cooler) use zipWith to get to the distances. After that, applying sum should be easy to finish the problem.

The cool way of making this list of pairs is to use a trick we use for the fibonacci numbers (fibs = 0 : 1 : zipWith (+) fibs (tail fibs)) of zipping a function with its tail. If you've not met it, zip works like this:

 ghci> zip [1..5] "Hello Mum"
[(1,'H'),(2,'e'),(3,'l'),(4,'l'),(5,'o')]
zip "Hello" "ello"
[('H','e'),('e','l'),('l','l'),('l','o')]
*Main> zip "Hello" (tail "Hello")
[('H','e'),('e','l'),('l','l'),('l','o')]

Awesome - that's exactly the trick you need.

Step 4: Lastly, write the code reading files to the data types you need

You'll need functions like

readPointsFile :: FilePath -> IO Points
readPointsFile fileName = do
    ....
    ....
    return (map readPoint pointStrings)

and then you can glue it together with, for example:

pathLengthFile :: FilePath -> FilePath -> IO Float
pathLengthFile pointsFilename pathFilename = do
     points <- readPointsFile pointsFilename
     path <- readPathFile pathFilename
     return (getPathLength points path)

Notice how hardly any of the logic is in this bit. You do all the real graft in pure code.


Secretly I'm a massive Applicative fan, and want to import Control.Applicative and write this as

pathLengthFile pointsFile pathFile = 
    getPathLength <$> readPointsFile pointsFile <*> readPathFile pathFile

But that's another lesson for a later day. :)

Upvotes: 8

Related Questions