将文件读入列表

时间:2014-12-09 19:24:44

标签: haskell

我刚刚开始学习Haskell,我想要做的是:

我有一个像这样的txt文件

2000
booka 500

bookb 1000

bookc 250

bookd 250

我希望我的输出像这样

booka 25%
bookb 50%
bookc 12.5%
bookd 12.5%

我可以用readFile从文件中读取conten,但我不知道如何处理后的内容

这就是我所拥有的

-- Imports needed
import System.IO
import Data.List

main = do
    putStrLn "Please insert the name of the file you want to process:"
    file <- getLine
    read_File file  

read_File file = do
h <- openFile file ReadMode
content <- readFile file

3 个答案:

答案 0 :(得分:1)

首先,您应该使用openFile打开文件,然后使用readFile读取文件。你只需要后者,它是一个方便的函数,可以打开文件,读取文件,返回内容,然后为你安全地关闭文件。

你应该写的是纯粹的功能

processContents :: String -> Maybe (Float, [(String, Float)])

可以从文件中获取第一个数字,然后是每个连续的行。由于我们希望确保代码是健壮的,因此我们也应该以某种方式处理故障。对于这个问题,我认为使用Maybe足以处理解析中的错误。为此,我们将添加几个导入:

import Text.Read (readMaybe)
import Data.Maybe

processContents :: String -> Maybe (Float, [(String, Float)])
processContents c = do
    let ls = lines c
    -- If we can't get a single line, the whole operation fails
    firstLine <- listToMaybe ls
    -- If we can't parse the total, the whole operation fails
    total <- readMaybe firstLine
    let rest = drop 1 ls
    return (total, catMaybes $ map parseLine $ rest)

listToMaybe函数充当&#34;安全头&#34;,它被定义为

listToMaybe (x:xs) = Just x
listToMaybe [] = Nothing

readMaybe函数的类型为Read a => String -> Maybe a,如果无法解析该值,则返回Nothing,这样就很容易使用了。 catMaybes函数会获取Maybe a个列表,提取所有不是Nothing的列表,并将其余内容作为类型为catMaybes :: [Maybe a] -> [a]的列表返回。

现在我们只需要实现parseLine

parseLine :: String -> Maybe (String, Float)
parseLine line = case words line of
    (book:costStr:_) -> do
        cost <- readMaybe costStr
        return (book, cost)
    _ -> Nothing

这会将空格分开,抓住前两个元素,并在可能的情况下将它们解析为名称和数字。

现在你有一个可以将你的示例文件解析为值

的函数
Just (2000.0, [("booka", 500.0), ("bookb", 1000.0), ("bookc", 250.0), ("bookd", 250.0)])

现在,您可以自行决定如何计算百分比并将其以所需格式打印到屏幕上。

答案 1 :(得分:1)

一种方法是将问题分解为三个明显的部分:

  1. 解析输入。
  2. 处理输入以获得结果。
  3. 打印结果。
  4. 实际上,几乎所有的数据处理问题都可以这样解决。

    解析输入

    解析输入实现读取文件,然后将内容分解为行列表。这些行中的第一行包含总值,其余行包含由标签和值组成的行表:

    parseFile :: FilePath -> IO (Float, [(String, Float)])
    parseFile fp = do
      contents <- readFile fp
      let (s : ss) = lines contents
      return (read s, parseTable ss)
    

    通过辅助函数解析表格,该函数将每一行分成两个单词的列表:一个用于标签,一个用于值。

    parseTable :: [String] ->   [(String, Float)]
    parseTable []        = []
    parseTable ("" : ss) = parseTable ss
    parseTable (s : ss)  = let [s1, s2] = words s
                           in (s1, read s2) : parseTable ss
    

    处理输入

    处理输入非常简单:

    processTable :: Float -> [(String, Float)] -> [(String, Float)]
    processTable sum table = [(label, (part / sum) * 100) | (label, part) <- table]
    

    表格中每行的第二个组成部分除以总值,然后再乘以100得到一个百分比。

    打印结果

    现在可以轻松获得单行的文本呈现,包括标签和百分比:

    showRow :: (String, Float) -> String
    showRow (label, perc) = label ++ " " ++ show perc ++ "%"
    

    将所有内容放在一起,然后处理整个文件:(1)解析它以获得总值和表,(2)处理表以获得已处理的表,以及(3)打印已处理的表行按行:

    processFile :: FilePath -> IO ()
    processFile fp = do
      (sum, table) <- parseFile fp
      mapM_ (putStrLn . showRow) (processTable sum table)
    

    对于您的示例数据,这会生成几乎您指定的输出:

    booka 25.0%
    bookb 50.0%
    bookc 12.5%
    bookd 12.5%
    

    也就是说,删除打印的浮点数中的尾随".0"是一个练习。 ;)

答案 2 :(得分:0)

要“存储”您可以调用的文件行:

fileContent <- readFile file
let fileLines = lines fileContent

从那里你可以为每一行使用words,它将返回[label,amount]的列表。您可以使用amount阅读read,将其重新置于某种整数或小数形式。

Live solution