1

从 IO String 到 String 有这样的区别吗

我想从 IO 中获取一些字符串值。

谁能告诉我这个问题。我一无所知

4

2 回答 2

6

问题是当程序运行时,您想对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 ()

因此,以一对这样的事情为例,考虑getLineputStrLn

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
于 2010-03-18T16:13:57.700 回答
4

IO 字符串是 IO-Monad 中的字符串。如果 IO-Monad 中的函数返回一个 IO 字符串,您可以通过以下方式获取该字符串:

do str <- ioFunc

当一个函数需要 IO 访问并且必须返回 IO 类型时,它就在 IO-Monad 中。

于 2010-03-18T08:36:38.523 回答