Haskell:使用IO()值过滤字典/散列/映射

时间:2012-02-13 04:13:25

标签: haskell

我有一个签名是String的函数 - >字符串 - > IO(可能是字符串) 现在,我使用此函数为字典构建值,最后得到:[(String,IO(Maybe String))]

我必须分析字典中的值并根据结果返回相应的键。我希望在地图上使用过滤器来逐步完成它,但我想不出一种方法可以动态提取IO动作。那么如何映射/过滤运行IO操作的字典并根据IO操作的结果返回字典的相应键?有没有一种简单的方法可以做到这一点,或者我只是弄得一团糟?

感谢。

2 个答案:

答案 0 :(得分:3)

或许解决方案是将sequence

一起使用
sequence . map (\(a,mb) -> (mb >>= \b -> return (a,b)))

然后您只需使用liftM将过滤器应用于生成的IO [(String,Maybe String)]

liftM位于Control.Monad中。或者,用符号表示

myFilter :: (String,(Maybe String)) -> Bool) 
            -> [(String,IO (Maybe String))] 
            -> IO [(String,(Maybe String))]
myFilter f ml =
   do
       l <- sequence . map (\(a,mb) -> (mb >>= \b -> return (a,b))) $ ml
       return $ filter f ml

也许有些重构是有道理的。通常在使用monad时,您希望使用mapM而不是map

Control.Monad中还有一个filterM函数。这可能就是你所需要的。


编辑:评论中指出

sequence . map (\(a,mb) -> (mb >>= \b -> return (a,b))) $ ml

相当于

mapM (\(a,mb) -> fmap ((,) a) mb) ml

所以

myFiter' f = (liftM $ filter f) .  mapM (\(a,mb) -> fmap ((,) a) mb)

答案 1 :(得分:0)

  

我有一个签名为String -> String -> IO (Maybe String)的函数现在,我使用此函数为字典构建值,最后得到:[(String,IO (Maybe String))]

这部分不适合我。如何仅输入2个字符串,最终得到类型为[(String, IO (Maybe String))]的有意义的结果?

缺乏细节,让我们假设你的情况是这样的:

f :: String -> String -> IO (Maybe String)
magic :: String -> (String, String)

magic获取一些键,并以某种方式将键拆分为f所需的两个输入字符串。因此,假设您有一个密钥列表,您最终会得到一个[(String, IO (Maybe String))],如下所示:

-- ks :: [String]
myMap = map (\k -> let (a,b) = magic k in (k, f a b)) ks

但是......如果我们有一个[(String, Maybe String)]而不是那么好吗?假设我们在IO行动中......

someIOAction = do
  ...
  myMap <- monadicMagicks (\k -> let (a,b) = magic k in (k, f a b)) ks

但我们可以使用monadicMagicks来使这项工作正确吗?让我们看一下我们对这些表达式的期望类型

monadicMagicks (\k -> ...) ks             :: IO [(String, Maybe String)]
(\k -> let (a,b) = magic k in (k, f a b)) :: String -> (String, IO (Maybe String))
ks                                        :: [String]

-- therefore
monadicMagicks :: (String -> (String, IO (Maybe String)))
               -> [String]
               -> IO [(String, Maybe String)]

停止...... Hoogle时间。让我们先概括一下我们正在寻找的东西。我们在这里有两种基本数据类型:String,它似乎是“输入”,Maybe String,它似乎是“输出”。因此,我们将String替换为a,将Maybe String替换为b。所以我们正在寻找(a -> (a, IO b)) -> [a] -> IO [(a, b)]。人力资源管理。未找到结果。好吧,如果我们只能获得IO [b]的结果类型,那么在do块中我们可以从IO中获取它,然后使用原始键列表zip。这也意味着我们输入的函数不必执行key与result的配对。因此,让我们简化我们正在寻找的内容,然后再试一次:(a -> IO b) -> [a] -> IO [b]。嘿,看看吧! mapM完全匹配该类型签名。

monadicMagicks :: (a -> IO b) -> [a] -> IO [(a, b)]
monadicMagicks f xs = do
  ys <- mapM f xs
  return $ zip xs ys

这实际上可以更简洁地编写,并且具有更通用的类型签名:

monadicMagicks :: Monad m => (a -> m b) -> [a] -> m [(a, b)]
monadicMagicks f xs = zip xs `liftM` mapM f xs

someIOAction = do
  ...
  -- Like we said, the lambda no longer has to pair up the key with the result
  myMap <- monadicMagicks (\k -> let (a,b) = magic k in f a b) ks

如果您对功能组合感到满意,那么最后一行也可以重写一点:

  myMap <- monadicMagicks (uncurry f . magic) ks

请注意monadicMagicks对某些monad可能没有多大意义,这可能就是它不在标准库中的原因。 (例如,在列表monad中使用mapM通常意味着结果的长度与输入的长度不同):

ghci> mapM (\x -> [x, -x]) [1,2]
[[1,2],[1,-2],[-1,2],[-1,-2]]