I'm trying to use the readFile function wrapped in a pure function.
Functions Used (In my school I have to re-write all built-in functions if I want to use it)
myNth :: [a] -> Int -> a
myNth [] n = error "error"
myNth (x:xs) 0 = x
myNth (_:xs) n = myNth xs (n - 1)
myTail :: [a] -> [a]
myTail (_:a) = a
myTail [] = error "empty list"
pointStrToInt :: String -> (Int, Int, Int)
pointStrToInt point = (read(myTail(myNth(split ',' point) 0)), read(myNth(split ',' point) 1), read(myNth(split ')' (myNth(split ',' point) 2)) 0))
fileToPointArray :: [String] -> [(Int, Int, Int)]
fileToPointArray = map pointStrToInt
split :: Eq a => a -> [a] -> [[a]]
split d [] = []
split d s = x : split d (drop 1 y) where (x,y) = span (/= d) s
Problem Function:
closest :: FilePath -> (Int, Int, Int) -> (Int, Int, Int)
closest path a = do
let content = readFile path
let closestPoint = pointStrToInt (myNth(lines (content)) 0)
--print (closestPoint)
let arr = fileToPointArray (lines content)
--print (arr)
let p = doClosest arr a closestPoint
--print (p)
p
where
doClosest :: [(Int, Int, Int)] -> (Int, Int, Int) -> (Int, Int, Int) -> (Int, Int, Int)
doClosest [] _ a = a
doClosest (x:xs) a (b, c, d) = if ((distance (itofPoint x) (itofPoint a)) < (distance (itofPoint x) (itofPoint (b, c, d))))
then (doClosest xs a x)
else (doClosest xs a (b, c, d))
The main objective is get the closest point to a given one passed as parameter. The prototype must be something like:
closest :: FilePath -> (Int, Int, Int) -> (Int, Int, Int)
But I can't use a impure function (readFile) due to the function is not IO().
How could i fix it?
Edit: -The file contains a set of points in (x,y,z) format
-The distance function return the distance of the 2 points
-The file can change in any execution
CodePudding user response:
You are already in a do bock; you just aren't taking advantage of it. content should be extracted from readFile path, not simply reference the IO action it creates. You'll also need to change the return value to IO (Int, Int, Int):
closest :: FilePath -> (Int, Int, Int) -> IO (Int, Int, Int)
closest path a = do
content <- readFile path
let closestPoint = pointStrToInt (myNth(lines (content)) 0)
let arr = fileToPointArray (lines content)
let p = doClosest arr a closestPoint
return p
where
doClosest :: [(Int, Int, Int)] -> (Int, Int, Int) -> (Int, Int, Int) -> (Int, Int, Int)
doClosest [] _ a = a
doClosest (x:xs) a (b, c, d) = if ((distance (itofPoint x) (itofPoint a)) < (distance (itofPoint x) (itofPoint (b, c, d))))
then (doClosest xs a x)
else (doClosest xs a (b, c, d))
A little simpler is to keep closest pure and combine it with readFile in a wrapper.
-- Note the order of arguments is reversed, so that we
-- can partially apply it later...
closest :: (Int, Int, Int) -> String -> (Int, Int, Int)
closest content a = let closestPoint = pointStrToInt (myNth(lines (content)) 0)
arr = fileToPointArray (lines content)
in doClosest arr a closestPoint
where
doClosest :: [(Int, Int, Int)] -> (Int, Int, Int) -> (Int, Int, Int) -> (Int, Int, Int)
doClosest [] _ a = a
doClosest (x:xs) a (b, c, d) = if ((distance (itofPoint x) (itofPoint a)) < (distance (itofPoint x) (itofPoint (b, c, d))))
then (doClosest xs a x)
else (doClosest xs a (b, c, d))
doIt :: FilePath -> (Int, Int, Int) -> IO (Int, Int, Int)
doIt path a = fmap (closest a) (readFile path)
(If you don't want to or can't alter the argument order in closest, you can use fmap (flip closest a) (readFile path) instead.)
