Reputation: 2532
I generated a list of tupules using some function and list comprehension, like this
[(x,y, func (x,y)) | x<-[xmin,(xmin+dx)..xmax], y<-[ymin,(ymin+dy)..ymax]]
So I pretty much created a list of 3D coordinates of many points.
Now my question is, how do I efficiently write this list in a file? I need it to be something like,
x0 y0 z0
x1 y1 z1
x2 y2 z2
....
Upvotes: 1
Views: 426
Reputation: 54068
To write to a file, you could do something like:
main = do
h <- openFile "output.txt" WriteMode
hPutStrLn h "Hello, file system"
hClose h
A safer way might be to use writeFile :: FilePath -> String -> IO ()
, which would close the handle if an error occurred, but you have to generate the entire contents first:
main = let contents = "Hello, " ++ "file" ++ " system"
in writeFile "output.txt" contents
I'll let you decide which you want to use. I would recommend the writeFile
method because of its safety.
Next, I would look at Data.List.intercalate
. If you're coming from an imperative language like Python, you might be familiar with the string join
method. In Python:
with open('output.txt', 'w') as f:
f.write(' '.join(['Hello,', 'file', 'system']))
this would write the string Hello, file system
to the file output.txt
. The intercalate
function is very similar in Haskell:
main = writeFile "output.txt" $ intercalate " " ["Hello,", "file", "system"]
-- This is the string to join by ----^ ^
-- These are the strings being joined together ------|
Now all you need to do is figure out how to turn your data into strings in order to write it out to a file. I would recommend writing a function
showTriple :: Show a => String -> (a, a, a) -> String
showTriple sep (x, y, z) = ???
which turns a triple into a sep
delimited string. That way you could easily swap that space out for a tab, comma, or whatever other symbol you might want to use.
If you get stuck, just edit your question with a progress update, showing your code and comment telling me what's got you stuck.
Now that you've solved the problem yourself, here's how I would do it using these functions:
-- This is just your list of data from above
range :: ((Double, Double) -> Double) -> (Double, Double, Double) -> (Double, Double, Double) -> [(Double, Double, Double)]
range func xs ys = [(x, y, func (x, y)) | x <- interval xs, y <- interval ys]
where
interval (tmin, tmax, dt)
| tmin < tmax = [tmin, tmin+dt .. tmax]
| otherwise = interval (tmax, tmin, dt)
writeDelimitedFile :: FilePath -> String -> [(Double, Double, Double)] -> IO ()
writeDelimitedFile file sep values = writeFile file $ delimitedString sep values
delimitedString :: String -> [(Double, Double, Double)] -> String
delimitedString sep values = intercalate "\n" $ map (showTriple sep) values
showTriple :: Show a => String -> (a, a, a) -> String
showTriple sep (x, y, z) = intercalate sep $ map show [x, y, z]
main :: IO ()
main = writeDelimitedFile "output.txt" " " $ range (uncurry (+)) (0, 10, 0.1) (0, 5, 0.1)
Of course, you could shorten it quite a bit without defining separate functions to do all the work and using unwords
and unlines
instead of intercalate
:
main :: IO ()
main = writeFile "output.txt" $ unlines
[unwords $ map show [x, y, z] |
(x, y, z) <- range (uncurry (+)) (0, 10, 0.1) (0, 5, 0.1)]
But this makes it more difficult to change later. And as you can see, if we want to keep it from marching off the side of the screen it has to be broken into multiple lines anyway.
Upvotes: 3