String 如何在Haskell中解析IO字符串?
我对哈斯克尔有意见。我的文本文件如下所示:String 如何在Haskell中解析IO字符串?,string,parsing,haskell,io,monads,String,Parsing,Haskell,Io,Monads,我对哈斯克尔有意见。我的文本文件如下所示: 5. 7. [(1,2,3),(4,5,6),(7,8,9),(10,11,12)]. 我不知道如何获得前两个数字(上面的2和7)和最后一行的列表。每行的末尾都有点 我试图构建一个解析器,但名为'readFile'的函数返回名为IO字符串的Monad。我不知道如何从那种类型的字符串中获取信息 我更喜欢在一系列角色上工作。可能有一个函数可以从“IO字符串”转换为[Char]?如果您有这种类型的解析器: myParser :: String ->
5.
7.
[(1,2,3),(4,5,6),(7,8,9),(10,11,12)].
我不知道如何获得前两个数字(上面的2和7)和最后一行的列表。每行的末尾都有点
我试图构建一个解析器,但名为'readFile'的函数返回名为IO字符串的Monad。我不知道如何从那种类型的字符串中获取信息
我更喜欢在一系列角色上工作。可能有一个函数可以从“IO字符串”转换为[Char]?如果您有这种类型的解析器:
myParser :: String -> Foo
您可以使用
readFile "thisfile.txt"
fmap myParser (readFile "thisfile.txt")
然后,您可以使用
readFile "thisfile.txt"
fmap myParser (readFile "thisfile.txt")
其结果将具有typeIO Foo
fmap
表示myParser
在IO内部运行
另一种思考方式是,鉴于
myParser::String->Foo
,fmap myParser::IO String->IO Foo
,,我认为您对Haskell中的IO有一个基本的误解。特别是你说:
也许有一个函数可以将“IO字符串”转换为[Char]
不,没有t1,事实上没有这样的函数是Haskell最重要的事情之一
哈斯克尔是一种非常有原则的语言。它试图区分“纯”函数(没有任何副作用,在输入相同时总是返回相同的结果)和“不纯”函数(有读取文件、打印到屏幕、写入磁盘等副作用)。这些规则是:
digitToInt :: String -> Int
getLine :: IO String
你知道这个函数是纯函数。如果给它一个字符串
,它将返回一个Int
,而且如果给它相同的字符串
,它将始终返回相同的Int
。另一方面,函数签名
digitToInt :: String -> Int
getLine :: IO String
是不纯的,因为String
的返回类型标记为IO
。显然,getLine
(读取一行用户输入)并不总是返回相同的字符串,因为它取决于用户键入的内容。您不能在纯代码中使用此函数,因为即使添加最小的杂质也会污染纯代码。一旦你去了IO
你就再也回不去了
您可以将IO
看作一个包装器。当您看到一种特定类型时,例如,x::IO String
,您应该将其解释为“x
是一种操作,在执行时执行一些任意I/O,然后返回类型为String
”的内容(请注意,在Haskell中,String
和[Char]
完全相同)
那么,您如何从IO
操作访问值呢?幸运的是,函数main
的类型是IO()
(这是一个执行一些I/O并返回()
,与不返回任何内容相同的操作)。因此,您可以始终在main
中使用IO
函数。当您执行Haskell程序时,您所做的是运行main
函数,这会导致程序定义中的所有I/O实际执行-例如,您可以读取和写入文件、请求用户输入、写入标准输出等
您可以考虑这样构建一个Haskell程序:
5.
7.
[(1,2,3),(4,5,6),(7,8,9),(10,11,12)].
- 所有执行I/O的代码都会获得
IO
标记(基本上,您将其放入do
块中)
- 不需要执行I/O的代码不需要位于
do
块中-这些是“纯”函数
- 您的
main
函数将您定义的I/O操作按顺序排列在一起,使程序执行您希望它执行的操作(在您喜欢的地方穿插纯函数)
- 当运行
main
时,会导致执行所有这些I/O操作
那么,考虑到所有这些,您如何编写程序?那么功能呢,
readFile :: FilePath -> IO String
lines:: String -> [String]
init :: [a] -> [a]
read :: (Read a) => String -> a
将文件读取为字符串
。所以我们可以用它来获取文件的内容。功能
readFile :: FilePath -> IO String
lines:: String -> [String]
init :: [a] -> [a]
read :: (Read a) => String -> a
在换行符上拆分一个字符串
,这样现在您就有了一个字符串
的列表,每个字符串对应于文件的一行。功能
readFile :: FilePath -> IO String
lines:: String -> [String]
init :: [a] -> [a]
read :: (Read a) => String -> a
从列表中删除最后一个元素(这将删除每行的最后一个
)。功能
readFile :: FilePath -> IO String
lines:: String -> [String]
init :: [a] -> [a]
read :: (Read a) => String -> a
获取字符串
并将其转换为任意Haskell数据类型,例如Int
或Bool
。合理地组合这些功能将为您提供程序
请注意,实际需要执行任何I/O的唯一时间是在读取文件时。因此,这是程序中唯一需要使用IO
标记的部分。程序的其余部分可以“纯”编写
听起来你需要的是这篇文章,它应该能解释你的很多问题。不要被“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)
运行::IO(Int,Int,[(Int,Int,Int)])
跑=做
内容作为一个编程工具,我也被IO
s弄糊涂了。只要记住,如果你去IO
你就永远不会出来。克里斯写了一封信。我只是想举一些例子来说明如何在monad中使用IO字符串
。我将使用它读取用户输入并返回一个IO字符串
line <- getLine
:type yourdata
text :: String
但是等等getLine
返回一个IO字符串
:type getLine
getLine :: IO String
那么,公司发生了什么事