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")
其结果将具有type
IO 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
    
    那么,公司发生了什么事