string - 如何在Haskell中解析IO字符串?

标签 string parsing haskell io monads

我对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有基本的误解。特别是,您这样说:

Maybe there is a function which can convert from 'IO String' to [Char]?



不,没有,没有这样的功能是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程序:
  • 所有执行I / O的代码都将获得IO标记(基本上,您将其放在do块中)
  • 不需要执行I / O的代码不需要在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数据类型,例如IntBool。明智地组合这些功能将为您提供程序。

    请注意,只有在读取文件时才真正需要执行任何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,因为顾名思义,它是非常不安全的!除非您真的知道自己在做什么,否则不要使用它。

    关于string - 如何在Haskell中解析IO字符串?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11229854/

    相关文章:

    java - 如何在 Java 中使用 XPath 读取 XML

    Haskell 乘以整数和实数

    haskell - Haskell 中的 'where' 子句中的内容是否只计算一次?

    c - 我的实现未能删除 c 中 String 的最后一个字符

    java - 如何获取并按空格分割输入的多个字符串行,然后将它们添加到Java中的arrayList?

    c - 如何根据c中的位置打印字符串?

    haskell - 给定列表长度、项目和填充项生成列表

    c - 如何 scanf ("%[^\n]", str);在 C 编程工作?

    javascript 如何解析数字?

    python - 如何解析文件并用其内容填充 python 字典