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.)