我正在尝试解决Haskell中的竞争性编程挑战问题。
这是我的代码:
module Main (main) where
import System.IO
import Text.Printf
getInt :: IO Int
getInt = readLn
getDouble :: IO Double
getDouble = readLn
getCoordinate :: (IO Double, IO Double, IO Double)
getCoordinate = (getDouble, getDouble, getDouble)
readCoordinates :: Int -> [(IO Double, IO Double, IO Double)] -> [(IO Double, IO Double, IO Double)]
readCoordinates 0 list = list
readCoordinates a list = readCoordinates (a - 1) list ++ [getCoordinate]
main :: IO ()
main = do
limit <- getInt
coordinates <- (readCoordinates limit [])
printf "%.2f\n" (run 0.0 (head coordinates) (tail coordinates))
run :: Double -> (Double, Double, Double) -> [(Double, Double, Double)] -> Double
run curr c1 (c2:cs) = run (curr + (d c1 c2)) c2 cs
run curr c1 [] = curr
d :: (Double, Double, Double) -> (Double, Double, Double) -> Double
d (x1, y1, z1) (x2, y2, z2) = sqrt (sas x1 x2) + (sas y1 y2) + (sas z1 z2)
sas :: Double -> Double -> Double
sas a1 a2 = (a1 - a2) ** 2
所以你可能猜到我正在读一个整数,它表示我应该读入多少3d坐标。然后我尝试读取所有这些并计算距离。
我收到很多错误,这里是错误日志:
Akvariet.hs:22:19:
Couldn't match type `[]' with `IO'
Expected type: IO (IO Double, IO Double, IO Double)
Actual type: [(IO Double, IO Double, IO Double)]
In the return type of a call of `readCoordinates'
In a stmt of a 'do' block:
coordinates <- (readCoordinates limit [])
In the expression:
do { limit <- getInt;
coordinates <- (readCoordinates limit []);
printf "%.2f" (run 0.0 (head coordinates) (tail coordinates)) }
Akvariet.hs:23:34:
Couldn't match expected type `[(Double, Double, Double)]'
with actual type `(IO Double, IO Double, IO Double)'
In the first argument of `head', namely `coordinates'
In the second argument of `run', namely `(head coordinates)'
In the second argument of `printf', namely
`(run 0.0 (head coordinates) (tail coordinates))'
Akvariet.hs:23:53:
Couldn't match expected type `[(Double, Double, Double)]'
with actual type `(IO Double, IO Double, IO Double)'
In the first argument of `tail', namely `coordinates'
In the third argument of `run', namely `(tail coordinates)'
In the second argument of `printf', namely
`(run 0.0 (head coordinates) (tail coordinates))'
我实际上无法真正地围绕IO类型,我知道它是不纯的并且每次都不会返回相同的东西,但我如何在我的程序中使用它?
我不明白readCoordinates方法是如何编译的,以及当main仍然是IO ()
类型时,为什么它不能将IO Double转换为Double。
干杯!
答案 0 :(得分:7)
首先,我建议您不读取数字,然后单独输入每个坐标。一次性读取所有输入(产生一个字符串)更容易,然后将其解析为坐标,而不必担心它是如何来自IO的。这看起来像
main = do
allInput <- getContents
let coordinates = parseCoords $ lines allInput
printf ...
与
type Vect = (Double, Double, Double)
parseCoords :: [String] -> [Vect]
parseCoords (x:y:z:cs) = (read x, read y, read z) : parseCoords cs
parseCoords _ = []
如果您希望手动阅读所有内容,以便对订单或其他内容进行精确控制,那么您需要正确使用IO
作为monad。将三个getDouble
组合成一个元组的IO动作是没什么用的;你真正想要的是一个单个 IO动作,它产生一个纯粹的坐标元组。
getCoordinate :: IO Vect
getCoordinate = do
x <- getDouble
y <- getDouble
z <- getDouble
return (x,y,z)
实际上这可以用Applicative写得更好,但我怀疑你发现上面的do
写作更容易理解:
getCoordinate' = liftA3 (,,) getDouble getDouble getDouble