将列表理解写入文件

时间:2014-02-22 04:53:33

标签: list haskell file-io list-comprehension

我使用一些函数和列表推导生成了一个元组列表,就像这样

[(x,y, func (x,y)) | x<-[xmin,(xmin+dx)..xmax], y<-[ymin,(ymin+dy)..ymax]]

所以我几乎创建了许多点的3D坐标列表。

现在我的问题是,如何在文件中有效地写入此列表?我需要它像,

x0 y0 z0
x1 y1 z1
x2 y2 z2
....

1 个答案:

答案 0 :(得分:3)

要写入文件,您可以执行以下操作:

main = do
    h <- openFile "output.txt" WriteMode
    hPutStrLn h "Hello, file system"
    hClose h

更安全的方法可能是使用writeFile :: FilePath -> String -> IO (),如果发生错误会关闭句柄,但您必须先生成整个内容:

main = let contents = "Hello, " ++ "file" ++ " system"
       in writeFile "output.txt" contents

我会让你决定要使用哪个。我会推荐writeFile方法,因为它的安全性。

接下来,我会看Data.List.intercalate。如果你来自像Python这样的命令式语言,你可能熟悉字符串join方法。在Python中:

with open('output.txt', 'w') as f:
    f.write(' '.join(['Hello,', 'file', 'system']))

这会将字符串Hello, file system写入文件output.txtintercalate函数在Haskell中非常相似:

main = writeFile "output.txt" $ intercalate " " ["Hello,", "file", "system"]
--         This is the string to join by ----^               ^
--         These are the strings being joined together ------|

现在您需要做的就是弄清楚如何将数据转换为字符串以便将其写入文件。我建议写一个函数

showTriple :: Show a => String -> (a, a, a) -> String
showTriple sep (x, y, z) = ???

将三元组转换为sep分隔的字符串。这样,您可以轻松地将该空格换成选项卡,逗号或您可能想要使用的任何其他符号。

如果您遇到困难,只需使用进度更新编辑您的问题,显示您的代码和评论,告诉我您遇到了什么问题。


既然你已经自己解决了这个问题,我就是这样使用这些函数的方法:

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

当然,如果没有定义单独的函数来完成所有工作并使用unwordsunlines而不是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)]

但是这使得以后更改变得更加困难。正如你所看到的,如果我们想让它远离屏幕一侧,那么它必须分成多行。