我有一个签名是String的函数 - >字符串 - > IO(可能是字符串) 现在,我使用此函数为字典构建值,最后得到:[(String,IO(Maybe String))]
我必须分析字典中的值并根据结果返回相应的键。我希望在地图上使用过滤器来逐步完成它,但我想不出一种方法可以动态提取IO动作。那么如何映射/过滤运行IO操作的字典并根据IO操作的结果返回字典的相应键?有没有一种简单的方法可以做到这一点,或者我只是弄得一团糟?
感谢。
答案 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]]