1

我有一个[字符]:

*Main> let message = "something"

我已经改组了字符串(你可以在这里找到完整的代码):

*Main> let key = shuffle message

*Main> message
"something"

*Main> :t message
message :: [Char]

*Main> key 
"goeimntsh"

*Main> :t key
key :: IO [Char]

现在我需要从这两个字符串创建 Data.Map 。就像是:

Map.fromList(zip message key)

但我什至不能压缩 [Char] 和 IO [Char]:

*Main> zip message key
<interactive>:1:13:
    Couldn't match expected type `[b0]' with actual type `IO [Char]'
    In the second argument of `zip', namely `key'
    In the expression: zip message key
    In an equation for `it': it = zip message key

我了解 shuffle 函数不会为相同的输入返回相同的结果。所以它必须返回 IO [Char]。

我了解我无法获取 Map.Map,并且我同意获取 IO Map.Map。但我需要像普通字符串一样使用 IO [Char]。我怎么才能得到它 ?

谢谢。

更新:

谢谢大家的解释。一些额外的问题:

@肯尼TM:

我理解 'do'- 表示法,但我需要一点时间来获得 liftM :) 但现在还有一个额外的问题:

*Main> let s = "abcdef"
*Main> let d = shuffle s
*Main> s
"abcdef"
*Main> d
"fedcba"
*Main> buildIOMap s d
fromList [('a','e'),('b','a'),('c','c'),('d','f'),('e','d'),('f','b')]
*Main> buildIOMap2 s d
fromList [('a','c'),('b','b'),('c','f'),('d','a'),('e','e'),('f','d')]
*Main> Map.fromList (zip "abcdef" "fedcba")
fromList [('a','f'),('b','e'),('c','d'),('d','c'),('e','b'),('f','a')]

'buildIOMap' 是一个 do-notation;'buildIOMap2' - 是一个 liftM - 实现。

为什么我在三种情况下得到不同的结果?

4

3 回答 3

4

在 GHCi 提示符下,您只需要执行

> key <- shuffle message

这将IO在右侧运行操作并将结果存储在 中key,现在它只是一个普通字符串。

> :t key
key :: [Char]

另一方面,当你写

> let key = shuffle message

您所做的只是命名操作,因此您可以多次运行它并获得不同的结果:

> :t key
key :: IO [Char]
> key
"ntimshgoe"
> key
"gimhntoes"

在 GHCi 中,评估值和运行操作之间的区别有些模糊,因为它允许您在同一个提示下执行两者。但是,如果您尝试以错误的方式(zip例如示例中的方式)混合它们,则会出现类型错误。

于 2012-10-30T06:53:29.010 回答
3

表达式key不是 a [Char]- 它是 a IO [Char]- 即将返回 a 的 IO 计算[Char]。以下是如何使用 shuffle 功能的示例:

main = do let message = "something"
          key <- shuffle message
          -- now key is a [Char]
          let m = Data.Map.fromList (zip message key)
          print m -- ... or whatever

从某种意义上说, 解<-IOfromIO [Char]并将返回的值放入 左边的变量中<-

于 2012-10-30T06:27:31.393 回答
3

我们可以为此创建一个 IO 函数。我们在这里使用这种do表示法是因为它对具有命令式语言背景的初学者更友好,但它并不是最紧凑的方式。

buildIOMap message key = do
    rawKey <- key                   -- unwrap an object inside the 'do' with '<-'
    let zipped = zip message rawKey -- Then 'zip' & 'Map.fromList' can be used 
    let map = Map.fromList zipped   --   normally.
    return map                      -- Finally we re-wrap the result into IO.

我们也可以通过更实用的方式来解决这个问题,即转换函数本身。请注意,表达式Map.fromList (zip message key)失败只是因为keyis anIO [Char]而不是 a [Char]

如果我们可以将函数Map.fromList (zip message __)从接受 a[Char]转换为 anIO [Char]那么它也可以工作。这种转换称为提升,当在 HaskellControl.Monad.liftM函数将执行此操作时:

liftM :: Monad m => (a -> r) -> (m a -> m r)

这将接受一个带有 1 个纯参数的函数并返回 1 个纯参数,并给出一个带有 1 个单子参数的函数并返回 1 个单子参数。

这里我们的纯函数是:

\x -> Map.fromList (zip message x)

所以我们也可以这样写:

buildIOMap message = liftM (\x -> Map.fromList (zip message x))

(注意:表示上述内容的更紧凑的方式是buildIOMap message = liftM $ Map.fromList . zip message

于 2012-10-30T06:32:45.027 回答