我对Haskell有问题。我的文本文件如下所示:
5.
7.
[(1,2,3),(4,5,6),(7,8,9),(10,11,12)].
我不知道如何从最后一行获得前两个数字(上面的2和7)和列表。每行末尾都有点。
我试图构建一个解析器,但名为“readFile”的函数返回名为IO String的Monad。我不知道如何从该类型的字符串中获取信息。
我更喜欢处理一系列字符。也许有一个函数可以将'IO String'转换为[Char]?
最佳答案
我认为您对Haskell的IO有基本的误解。特别是,您这样说:
不,没有,没有这样的功能是Haskell最重要的事情之一。
Haskell是一种非常有原则的语言。它试图在“纯”函数(没有任何副作用,并且在提供相同输入时始终返回相同结果)与“不纯”函数(例如从文件读取,打印的副作用)之间保持区别。到屏幕,写入磁盘等)。规则是:
将代码标记为纯或不纯的方式是使用类型系统。当您看到类似的功能签名时
digitToInt :: String -> Int
您知道此功能是纯函数。如果给它一个
String
,它将返回一个Int
,而且如果给它一个相同的Int
,它将始终返回相同的String
。另一方面,像getLine :: IO String
是不纯的,因为
String
的返回类型标记为IO
。显然getLine
(读取一行用户输入)将不会总是返回相同的String
,因为它取决于用户键入的内容。您不能在纯代码中使用此函数,因为即使添加最小的杂质位也会污染纯代码。一旦输入IO
,就永远无法返回。您可以将
IO
视为包装器。当您看到特定的类型(例如x :: IO String
)时,应将其解释为“x
是一个 Action ,执行该 Action 时会执行任意I / O,然后返回类型为String
的东西”(请注意,在Haskell中,String
和[Char]
是完全一样的东西)。那么,您如何从
IO
操作中访问值?幸运的是,函数main
的类型为IO ()
(这是执行一些I / O并返回()
的操作,这与不返回任何内容一样)。因此,您始终可以在IO
中使用main
函数。当您执行Haskell程序时,您正在执行的是main
函数,该函数会导致程序定义中的所有I / O都被实际执行-例如,您可以从文件中读取和写入文件,要求用户输入信息,写入标准输出等您可以考虑如下构造Haskell程序:
IO
标记(基本上,您将其放在do
块中)do
块中-这些是“纯”函数。 main
函数将您定义的I / O操作按顺序排列在一起,以使程序可以执行您想要的操作(穿插在任意位置的纯函数)。 main
时,将导致所有这些I / O操作被执行。 那么,既然如此,您如何编写程序?好吧,功能
readFile :: FilePath -> IO String
读取一个文件作为
String
。因此,我们可以使用它来获取文件的内容。功能lines:: String -> [String]
在换行符上分割一个
String
,所以现在您有了String
的列表,每个列表对应于文件的一行。功能init :: [a] -> [a]
从列表中删除最后一个元素(这将摆脱每行最后的
.
)。功能read :: (Read a) => String -> a
接受
String
并将其转换为任意的Haskell数据类型,例如Int
或Bool
。明智地组合这些功能将为您提供程序。请注意,只有在读取文件时才真正需要执行任何I / O。因此,这是程序中唯一需要使用
IO
标记的部分。程序的其余部分可以“完全”编写。听起来您需要的是The IO Monad For People Who Simply Don't Care文章,它应该可以解释您的许多问题。不要为“monad”这个术语感到恐惧-您不需要了解编写Haskell程序的monad是什么(请注意,本段是我答案中唯一使用“monad”一词的段。现在已经使用了四次...)
这是(我认为)您要编写的程序
run :: IO (Int, Int, [(Int,Int,Int)])
run = do
contents <- readFile "text.txt" -- use '<-' here so that 'contents' is a String
let [a,b,c] = lines contents -- split on newlines
let firstLine = read (init a) -- 'init' drops the trailing period
let secondLine = read (init b)
let thirdLine = read (init c) -- this reads a list of Int-tuples
return (firstLine, secondLine, thirdLine)
要回答有关将
npfedwards
应用于lines
的输出的readFile text.txt
注释,您需要意识到readFile text.txt
给您提供了IO String
,并且仅当您将其绑定(bind)到变量(使用contents <-
)时,您才能访问基础String
,以便您可以将lines
应用于它。请记住:一旦您输入
IO
,就永远不会返回。1我故意忽略
unsafePerformIO
,因为顾名思义,它是非常不安全的!除非您真的知道自己在做什么,否则不要使用它。