2

我有一个定义了一些笛卡尔坐标的文件(points.txt):

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

我有第二个文件 (routes.txt),其中包含基于 points.txt 中的点定义的路线。

route1 ACDB
route2 ABC

我需要找到每条路线的长度。到目前为止,我计算了两点之间的距离,如下所示:

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)

如何计算路线的完整距离?

另外,如果我有几条路线,我怎样才能找到最长的一条?

提前致谢。

4

1 回答 1

8

第1步:分离纯代码和非纯代码,直到最后才写IO。

最简单的方法是先用纯代码解决你的问题,然后再添加文件读取。否则你会很想写很多 IO 代码。

如果将名称与坐标分开会更容易:

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

然后有一些练习数据:

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

第 2 步:编写一个函数,该函数接受点集合和一对点名称并返回距离

首先,您需要一个带有名称并为您提供坐标的函数。

coordinates :: Points -> Name -> Coordinates

如果我们使用[Point],最简单的方法是使用lookup. (您可以像这样或通过像这样的类型在 hoogle 上找到有关函数的信息,尽管没有明显的方法让您知道您想要一个Maybe,并且当您只搜索 时[(a,b)] -> b,查找是一个很长的路要走。)

如果您需要此步骤的帮助,请发表评论。

使用它,您将能够编写

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

第 3 步:将表示路径的名称列表转换为点名称对列表

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

或(冷却器)用于zipWith到达距离。之后,应用sum应该很容易完成问题。

制作这个对列表的很酷的方法是使用我们用于斐波那契数 ( fibs = 0 : 1 : zipWith (+) fibs (tail fibs)) 的技巧,即用尾部压缩函数。如果你还没有遇到过,zip的工作方式是这样的:

 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')]

太棒了——这正是你需要的技巧。

第四步:最后将代码读取文件写入你需要的数据类型

你需要像这样的功能

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

然后你可以将它粘合在一起,例如:

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

注意这个位中几乎没有任何逻辑。你用纯代码做所有真正的移植。


秘密地,我是一个忠实的Applicative粉丝,并想把import Control.Applicative它写成

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

但这是以后的另一个教训。:)

于 2013-06-09T22:53:44.360 回答