当前位置: 首页 > 知识库问答 >
问题:

如何解析Haskell中的IO字符串?

严俊友
2023-03-14

我对哈斯克尔有意见。我有这样的文本文件:

5.
7. 
[(1,2,3),(4,5,6),(7,8,9),(10,11,12)].

我不知道如何获得前两个数字(上面的2和7)和最后一行的列表。每行的末尾都有点。

我试图构建一个解析器,但名为readFile的函数返回名为IO String的Monad。我不知道如何从那种字符串中获取信息。

我更喜欢在一系列角色上工作。也许有一个函数可以将“IO字符串”转换为[Char]?

共有3个答案

百里杰
2023-03-14

正如人们已经说过的,如果你有两个函数,一个是readStringFromFile:: FilePath-

fmap表示IOIOFunctor):

fmap doTheRightThingWithString readStringFromFile

使用(

import Control.Applicative

...

doTheRightThingWithString <$> readStringFromFile

使用liftM进行IOliftM==fmap):

import Control.Monad

...

liftM doTheRightThingWithString readStringFromFile

使用(

readStringFromFile >>= \string -> return (doTheRightThingWithString string)
readStringFromFile >>= \string -> return $ doTheRightThingWithString string
readStringFromFile >>= return . doTheRightThingWithString
return . doTheRightThingWithString =<< readStringFromFile

使用do符号:

do
  ...
  string <- readStringFromFile
  -- ^ you escape String from IO but only inside this do-block
  let result = doTheRightThingWithString string
  ...
  return result

每次你都会得到IO一些东西

你为什么要这样做?好的,有了它,您的语言中就有了纯粹的、引用透明的程序(函数)。这意味着,每一个类型为IO自由的函数都是纯的、引用透明的,因此对于相同的参数,它将返回相同的值。例如,doTheRightThingWithString将为相同的字符串返回相同的某物。但是,非IO自由的readStringFromFile每次都可以返回不同的字符串(因为文件可以更改),因此您无法从IO中逃逸这种不纯净的值。

崔高远
2023-03-14

作为一个编程高手,我也被IOs搞糊涂了。只要记住,如果你去IO你永远不会出来。克里斯写了一篇很好的解释。我只是想举一些例子来说明如何在monad中使用IO字符串。我将使用getLine,它读取用户输入并返回一个IO字符串

line <- getLine 

所有这些操作都是将用户输入从getLine绑定到名为line的值。如果在ghci中键入此项,并键入:type line,它将返回:

:type line
line :: String

但是等等getLine返回一个IO字符串

:type getLine
getLine :: IO String

那么,来自getLineIOness发生了什么变化<代码>

main = do
    putStrLn "How much do you love Haskell?"
    amount <- getLine
    putStrln ("You love Haskell this much: " ++ amount) 

如果你像我一样,你很快就会发现liftIO是你下一个最好的monad朋友,并且$有助于减少你需要写的括号数量。

那么如何从readFile获取信息呢?如果readFile的输出是IO字符串,如下所示:

:type readFile
readFile :: FilePath -> IO String

那么你只需要友好的

 yourdata <- readFile "samplefile.txt"

现在,如果在ghci中键入它并检查yourdata的类型,您会注意到它是一个简单的字符串。

:type yourdata
text :: String

鱼志诚
2023-03-14

我认为你对哈斯克尔的信息办公室有一个根本性的误解。特别是,你这样说:

也许有一个函数可以从IO字符串转换为[Char]?

不,没有1,事实上没有这样的函数是Haskell最重要的事情之一。

哈斯克尔是一种非常有原则的语言。它试图区分“纯”函数(没有任何副作用,在输入相同时总是返回相同的结果)和“不纯”函数(有读取文件、打印到屏幕、写入磁盘等副作用)。这些规则是:

  1. 您可以在任何地方使用纯函数(在其他纯函数或非纯函数中)
  2. 只能在其他不纯函数中使用不纯函数

代码被标记为纯或不纯的方式是使用类型系统。当你看到一个函数签名,比如

digitToInt :: String -> Int

你知道这个函数是纯函数。如果给它一个字符串,它将返回一个Int,而且如果给它相同的字符串,它将始终返回相同的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实际上都被执行——例如,你可以从文件中读写,询问用户输入,写信给stdout等。

您可以考虑这样构建一个Haskell程序:

  • 所有执行I/O的代码都将获得IO标记(基本上,您将其放在do块中)
  • 不需要执行I/O的代码不需要在do块中-这些是纯函数。
  • 您的main函数序列将您定义的I/O操作按顺序组合在一起,使程序做您想让它做的事情(在您喜欢的任何地方穿插纯函数)。
  • 当您运行main时,您将导致所有这些I/O操作被执行。

那么,考虑到所有这些,你如何编写你的程序?嗯,功能

readFile :: FilePath -> IO String

String的形式读取文件。所以我们可以用它来获取文件的内容。的功能

lines:: String -> [String]

在换行符上拆分一个字符串,这样现在您就有了一个字符串的列表,每个字符串对应于文件的一行。功能

init :: [a] -> [a]

从列表中删除最后一个元素(这将删除每行的最后一个)。功能

read :: (Read a) => String -> a

获取一个String并将其转换为任意Haskell数据类型,例如IntBool。将这些功能合理地结合起来将为您提供程序。

请注意,您实际需要执行任何I/O的唯一时间是在读取文件时。因此,这是程序中唯一需要使用IO标记的部分。程序的其余部分可以“纯粹”地编写。

听起来你需要的是这篇文章《根本不在乎的人的单子》,这篇文章应该能解释你的很多问题。不要被“monad”这个词吓到——你不需要理解什么是monad来编写Haskell程序(请注意,这一段是我答案中唯一使用“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)

要回答关于将line应用于readFiletext.txt的输出的npfedwards注释,您需要意识到readFiletext.txt为您提供了一个IO String>,并且只有当您将其绑定到变量时(使用内容)

记住:一旦你去了IO,你就再也回不去了。

1我故意忽略了unsafePerformIO,因为正如名字所暗示的,它是非常不安全的!除非你真的知道自己在做什么,否则永远不要使用它。

 类似资料:
  • 问题内容: 我想要一个Cookie字符串(因为它可能会在Set-Cookie标头中返回),并且能够轻松修改其中的一部分,特别是到期日期。 我看到有几种不同的Cookie类,例如BasicClientCookie,但是我看不到任何简单的方法来将字符串解析为这些对象之一。 我在api级别9中看到他们添加了具有解析方法的HttpCookie,但是我需要在以前的版本中工作。 有任何想法吗? 谢谢 问题答案

  • 我有一根绳子 然后我想将其解析为sqlDate。并将其插入数据库。 是否可以将该字符串解析为sqlDate?!?! 是的,sql日期格式是“yyyy-mm-dd”

  • 问题内容: 我想从android中的字符串解析网址。示例字符串为 我想在不使用subString方法的情况下从String 解析URL 。 问题答案: 更新: 您可以使用正则表达式和正则表达式来查找文本中的所有url。 原版的: 您可以使用功能。

  • 问题内容: 给定字符串,获取代表它的DOM元素的(最简单)方法是什么? 问题答案: 我在某个地方找到了(不记得在哪里):

  • 问题内容: 前缀/ dir1 / dir2 / dir3 / dir4 / .. 如何在Java中从上述字符串中解析出值? 这里的前缀可以是: / usr / local / apache2 / resumes 问题答案: 如果要在字符处分割,该方法将起作用: 例如: 输出量 编辑 前缀为a 的情况,我们知道前缀是什么: 没有前缀的子字符串由方法组成。也就是说,然后通过运行。 输出: 重新编辑 如

  • 问题内容: 有没有一种方法可以将Typescript中的字符串解析为JSON。 示例:在JS中,我们可以使用。Typescript中有类似的功能吗? 我有一个JSON对象字符串,如下所示: 问题答案: Typescript是javascript(的超集),因此您可以像在javascript中那样使用它: 只有在打字稿中,您才能对结果对象进行打字: (操场上的代码)