我对哈斯克尔有意见。我有这样的文本文件:
5.
7.
[(1,2,3),(4,5,6),(7,8,9),(10,11,12)].
我不知道如何获得前两个数字(上面的2和7)和最后一行的列表。每行的末尾都有点。
我试图构建一个解析器,但名为readFile的函数返回名为IO String的Monad。我不知道如何从那种字符串中获取信息。
我更喜欢在一系列角色上工作。也许有一个函数可以将“IO字符串”转换为[Char]?
正如人们已经说过的,如果你有两个函数,一个是readStringFromFile:: FilePath-
用
fmap
表示IO
(IO
是Functor
):
fmap doTheRightThingWithString readStringFromFile
使用
(
import Control.Applicative
...
doTheRightThingWithString <$> readStringFromFile
使用
liftM
进行IO
(liftM==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
中逃逸这种不纯净的值。
作为一个编程高手,我也被IO
s搞糊涂了。只要记住,如果你去IO
你永远不会出来。克里斯写了一篇很好的解释。我只是想举一些例子来说明如何在monad中使用IO字符串
。我将使用getLine,它读取用户输入并返回一个IO字符串
。
line <- getLine
所有这些操作都是将用户输入从getLine
绑定到名为line
的值。如果在ghci中键入此项,并键入:type line
,它将返回:
:type line
line :: String
但是等等getLine
返回一个IO字符串
:type getLine
getLine :: IO String
那么,来自getLine
的IO
ness发生了什么变化<代码>
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
我认为你对哈斯克尔的信息办公室有一个根本性的误解。特别是,你这样说:
也许有一个函数可以从IO字符串转换为[Char]?
不,没有1,事实上没有这样的函数是Haskell最重要的事情之一。
哈斯克尔是一种非常有原则的语言。它试图区分“纯”函数(没有任何副作用,在输入相同时总是返回相同的结果)和“不纯”函数(有读取文件、打印到屏幕、写入磁盘等副作用)。这些规则是:
代码被标记为纯或不纯的方式是使用类型系统。当你看到一个函数签名,比如
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数据类型,例如Int
或Bool
。将这些功能合理地结合起来将为您提供程序。
请注意,您实际需要执行任何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中那样使用它: 只有在打字稿中,您才能对结果对象进行打字: (操场上的代码)