我刚刚开始学习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
答案 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)
一种方法是将问题分解为三个明显的部分:
实际上,几乎所有的数据处理问题都可以这样解决。
解析输入实现读取文件,然后将内容分解为行列表。这些行中的第一行包含总值,其余行包含由标签和值组成的行表:
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
,将其重新置于某种整数或小数形式。