从 IO String 到 String 有这样的区别吗
我想从 IO 中获取一些字符串值。
谁能告诉我这个问题。我一无所知
问题是当程序运行时,您想对String
您的操作将产生的值做什么。IO String
你写的好像你已经把手放在弦上了,但你没有。
或者:你写得好像你已经定义或隔离了一个字符串,但你没有。到目前为止,您已经定义或隔离了一个返回字符串的操作。执行该操作的个别情况将返回不同的字符串。
大概您正在尝试定义一个更复杂的操作——某种类型的IO Blah
东西——也许某种IO ()
类型的东西可以编译成可执行文件的类型。这个想法是,在更复杂的动作中,String
通过执行类型的动作获得一个值IO String
——你到目前为止定义的动作——复杂动作的执行者将继续做一些取决于什么的事情那个值是。这是由类型的函数表示的东西String -> IO Blah
——也许String -> IO ()
当然,这样的函数不会将IO String
值(即返回字符串的操作)作为参数,而是String
将值作为参数。我们不能直接加入他们。
要从返回字符串(您的IO String
值)和 'String -> IO Blah' 函数的操作中获得新的东西(返回 blah的操作), 我们通过函数将它们连接起来>>=
。专门针对这种情况>>=
的类型IO String -> (String -> IO Blah) -> IO Blah
- 它可能是IO String -> (String -> IO ()) -> IO ()
因此,以一对这样的事情为例,考虑getLine
和putStrLn
。
getLine
是找出刚刚输入的字符串的动作——它有类型IO String
。
您可能会说在屏幕上putStrLn
打印一个String
值,然后返回到左边距。但这是肤浅的:做什么明确的事情取决于一个String
值的规范,所以它有 type String -> IO()
。也就是说:putStrLn
不做任何事情,它是一个将字符串映射到可以做的事情的函数。IO ()
因此,像往常一样,您可以通过在函数符号后面加上域类型(字符串,即)中的某物的名称 来定义其范围类型中的值(动作,即's String
)因此该组合putStrLn "Gee whiz"
命名了一个明确的动作,某物类型IO ()
.
ghci
将即时执行此类操作,因此,对于您可以编写的任何字符串,例如“Gee whiz” putStrLn "Gee whiz"
,它会立即“执行此操作”——将“Gee whiz”写入屏幕并返回到左边距的操作。
Prelude> putStrLn "Gee whiz"
Gee whiz
Prelude>
类似地,只有 Unix 钟字符的单字符字符串\BEl
是我们用'\BEl':[]
or['\BEL']
或命名的字符串"\BEL"
。对于作为参数的那个字符串,putStrLn
对于一个值有一种听上去完全不同的动作。我们得到
Prelude> putStrLn "\BEL"
Prelude>
在这里,您将听到 Unix 铃声在它返回到左边距之前响起。这是一个相当蹩脚的音频程序,但你就在那里。 ghci
正在执行敲响 Unix 钟声putStrLn "\BEL"
的动作,这是您在按回车之前用单词命名的动作。
所以无论如何getLine
是一个类型的值,IO String
你想“从 IO 中获取这个字符串值”。当然,它还不存在,这取决于用户键入的内容。但是我们可以考虑程序在得到这样一个值时要做什么。我们可以通过指定从字符串到操作的函数来指定这一点,例如putStrLn
. >>=
因此,我们可以定义一个完整的动作,它“获取一个值”并通过使用或do
符号糖 组合它们以某种方式使用它。
最简单的情况是这样的echo
:
echo :: IO ()
echo = getLine >>= putStrLn
或等效地
echo = getLine >>= (\x -> putStrLn x)
或do
表示:
echo = do
the_string_i_want_to_take <- getLine
putStrLn the_string_i_want_to_take
或者不那么荒谬:
echo = do
x <- getLine
putStrLn x
当然,你想“拿走绳子”,也许在完成之前把它弄乱。
reverseEcho :: IO ()
reverseEcho = getLine >>= (\x -> putStrLn (reverse x))
或更简洁:
reverseEcho = getLine >>= (putStrLn . reverse)
或do
表示:
reverseEcho = do
the_string_i_want_to_take <- getLine
putStrLn (reverse the_string_i_want_to_take)
或者不那么荒谬:
reverseEcho = do
x <- getLine
putStrLn (reverse x)
如果您想将“反转字符串”视为在获取和打印之间使用字符串完成的操作,您可以编写:
reverseEcho = do
the_string_i_want_to_take <- getLine
the_string_after_i_have_processed_it <- return (reverse the_string_i_want_to_take)
putStrLn (the_string_after_i_have_processed_it)
或者
reverseEcho = do
x <- getLine
y <- return x
putStrLn y
或等效地
reverseEcho = (getLine >>= (return . reverse)) >>= putStrLn
这里括号不是必需的,因为 和 的优先级.
已经过>>=
适当优化。但这(getLine >>= (return . reverse))
只是“返回字符串的操作”的另一个名称,IO String
它的值不是字符串本身。您不能String -> Whatever
直接对其应用函数来获得 a Whatever
,但您可以将它与从字符串到操作的函数结合使用>>=
.
相似地
reverseFileAA :: IO ()
reverseFileAA = readFile "AA.txt" >>= writeFile "reversedAA.txt" . reverse
是写一个名为“reversedAA.txt”的文件的动作,它与在 中找到的字符串相反AA.txt
,无论它是什么,并且可能被写入
reverseFileAA = do
old_file_contents <- readFile "AA.txt"
new_file_contents <- return (reverse old_file_contents)
writeFile "reversedAA.txt" old_file_contents
IO 字符串是 IO-Monad 中的字符串。如果 IO-Monad 中的函数返回一个 IO 字符串,您可以通过以下方式获取该字符串:
do str <- ioFunc
当一个函数需要 IO 访问并且必须返回 IO 类型时,它就在 IO-Monad 中。