从 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 中。