【发布时间】:2010-03-18 08:23:28
【问题描述】:
IO String 和 String 有没有这样的区别
我想从 IO 中获取一些字符串值。
谁能告诉我这个问题。我一无所知
【问题讨论】:
-
你已经看过book.realworldhaskell.org了吗?它可能有助于提供更多见解。
IO String 和 String 有没有这样的区别
我想从 IO 中获取一些字符串值。
谁能告诉我这个问题。我一无所知
【问题讨论】:
问题是,当程序运行时,您想对IO String 操作将产生的String 值做什么。你写的好像你已经把手放在弦上了,但你没有。
或者:你写的好像你已经定义或隔离了一个字符串,但你没有。到目前为止,您已经定义或隔离了一个返回字符串的动作。执行该操作的个别情况将返回不同的字符串。
假设您正在尝试定义一个更复杂的操作——IO Blah 类型的东西——也许IO () 类型的东西可以编译为可执行文件的类型。这个想法是,在更复杂的动作中,通过执行 IO String 类型的动作(您到目前为止定义的动作)到达 String 值 - 复杂动作的执行者将继续执行这取决于该值是什么。这是由String -> IO Blah 类型的函数表示的东西——可能是String -> IO ()
当然,这样的函数不会将IO String 值(即返回字符串的操作)作为参数,而是将String 值作为参数。我们不能直接加入他们。
从返回字符串的动作——你的IO String值——和一个'String -> IO Blah'函数,到一些新的东西——一个动作返回一个废话——我们通过函数>>= 加入它们。专门针对这种情况的>>= 具有IO String -> (String -> IO Blah) -> IO Blah 类型——它可能是IO String -> (String -> IO ()) -> IO ()
因此,举一个最简单的例子,考虑getLine 和putStrLn。
getLine 是找出刚刚输入的字符串的操作——它的类型为IO String。
您可能会说putStrLn 将String 值打印到屏幕上,然后返回到左边距。但这是肤浅的:具体做什么取决于String 值的规范,因此它的类型为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':[] 或 ['\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 函数直接应用于它以获得Whatever,但您可以将它与通过>>= 从字符串到actions 的函数结合起来。
同样
reverseFileAA :: IO ()
reverseFileAA = readFile "AA.txt" >>= writeFile "reversedAA.txt" . reverse
是用在AA.txt 中找到的字符串的反向写入一个名为“reversedAA.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 中。
【讨论】: