【问题标题】:Whats the difference between IO String and normal String in HaskellHaskell中的IO String和普通String有什么区别
【发布时间】:2010-03-18 08:23:28
【问题描述】:

IO String 和 String 有没有这样的区别

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

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

【问题讨论】:

标签: haskell types monads


【解决方案1】:

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

因此,举一个最简单的例子,考虑getLineputStrLn

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" 命名的动作。

所以无论如何getLineIO 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 

这里的括号不是必需的,因为.&gt;&gt;= 的优先级已经过适当优化。但是(getLine &gt;&gt;= (return . reverse))只是“返回字符串的动作”的别称,值IO String,本身并不是字符串。您不能将String -&gt; Whatever 函数直接应用于它以获得Whatever,但您可以将它与通过&gt;&gt;= 从字符串到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

【讨论】:

    【解决方案2】:

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

    do str <- ioFunc
    

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-03-26
      • 1970-01-01
      • 2016-11-09
      • 2012-08-11
      • 2018-06-07
      • 2012-10-06
      • 2021-12-21
      相关资源
      最近更新 更多