2012-11-01 61 views
4

我有两个功能:如何避免重复的功能(IO而不是-IO版本)

trans_table :: [Char] -> [Char] -> Map.Map Char Char 
trans_table s1 s2 = Map.fromList (zip s1 s2) 

random_trans_table :: IO (Map.Map Char Char) 
random_trans_table = do 
    rawKey <- shuffle ascii 
    let zipped = zip ascii rawKey 
    let map = Map.fromList zipped 
    return map  

他们首先创建了两个字符串地图;第二个生成随机地图。 第一个返回Map.Map Char Char;第二个返回IO(Map.Map字符字符)

现在我需要从这个地图查找价值,我已经创建了两个功能 - 一个用于IO地图和一个用于地图:

translate_char :: Map.Map Char Char -> Char -> Maybe Char 
translate_char table c = Map.lookup c table 

translate_char_io :: IO (Map.Map Char Char) -> Char -> IO (Maybe Char) 
translate_char_io table c = do 
    raw_table <- table 
    let result = Map.lookup c raw_table 
    return result 

我不不喜欢它,因为它会导致代码重复。我已经复制了一个函数,如果我通过这种方式编码,我将需要复制我所有的函数。

有没有一种方法来创建函数,它将与Map和IO(Map)一起工作?

+8

你几乎从来没有想写采取'IO Something'作为输入,除非你正在写某种控制结构这就需要如果/当/动作运行多少次控制功能。如果你只需要一个值,你应该让你的函数只取一个值,并在调用站点使用'do'-notation,'>> ='或类似的方法来绑定第一个动作的结果并将其传递给第二个。 – hammar

回答

8

do -notation,或它去糖的“绑定”运算符>>=,为您处理此问题。

任何你可能称为translate_char_io的地方,都可以通过让monad语法为你解开你的表格来调用纯函数。例如,如果你想创建一个随机表,并期待两个不同的人物在里面,你可以这样来做:

test c1 c2 = do 
    table <- random_trans_table 
    return (translate_char table c1, translate_char table c2) 
4

您可以使用liftMimport Control.Monad)写一个转换器:

translate_char_io table c = liftM (\table' -> translate_char table' c) table 

请注意,为什么您只希望table参数为IO,但它是一个选项。翻转参数也可以让你摆脱liftM (translate_char c) table的内部函数。如果你想两个参数来作为IO行动,您还可以使用liftM2

translate_get_char = liftM2 translate_char random_trans_table getChar 

或者你可以只使用DO-符号来使用你的纯函数在IO代码,这可能是在这种情况下,最简单的:

translateAndPrint = do 
    table <- random_trans_table 
    char <- getChar 
    putChar (translate_char table char)