2012-10-30 68 views
0

我有一個[字符]:拉鍊[字符]和IO [字符]

*Main> let message = "something" 

,我已經打亂字符串(完整的代碼,你可以找到here):

*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) 

但我甚至無法拉鍊[字符]和IO [字符]:

*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 

據我所知,隨機播放功能沒有對相同的輸入返回相同的結果。所以它必須返回IO [Char]。

我明白我無法獲得Map.Map,我同意獲得IO Map.Map。但我需要像普通字符串一樣使用IO [Char]。我怎麼才能得到它 ?

謝謝。

更新時間:

感謝大家對你的解釋。一些額外的問題:

@KennyTM:

我明白了「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'是一個符號; 'buildIOMap2' - 是liftM - 的實現。

爲什麼我在三種情況下得到不同的結果?

+0

你可以使用'do'符號並提取裏面的'key'或者''liftM''zip'的結果... – kennytm

+0

對不起,我是Haskell的新手。你能舉個例子嗎? – demas

回答

2

我們可以爲此創建一個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)僅因爲keyIO [Char]而不是[Char]而失敗。

如果我們可以將函數Map.fromList (zip message __)從接受[Char]轉換爲IO [Char]那麼它也可以工作。這種轉變被稱爲提升,在Haskell the Control.Monad.liftM function時會做到這一點:

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

+0

感謝您的回答。我已經更新了我的問題 - 你能解釋一些時刻嗎? – demas

+0

@demas:[@hammar](http://stackoverflow.com/a/13134053/224671)已經回答了你。 – kennytm

+0

再次謝謝你 - 你真的幫我:) – demas

3

表達式key不是[Char] - 它是IO [Char] - 即將返回[Char]的IO計算。這裏有一個如何使用您的隨機播放功能的例子:

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 

在某種意義上,<-解開從IO [Char]IO和返回值放入變量向左<-的。

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示例類似的錯誤方式混用它們,則會出現類型錯誤。