0

以下功能如何工作:

mapEithers :: (a -> Either b c) -> [a] -> Either b [c]
mapEithers f (x:xs) = case mapEithers f xs of
                        Left err -> Left err
                        Right ys -> case f x of
                                      Left err -> Left err
                                      Right y -> Right (y:ys)
mapEithers _ _ = Right []

在第一个 case 表达式 ( ) 中,当函数尚未应用于列表的元素时case mapEithers f xs,它如何与Left值进行模式匹配。Rightf

4

2 回答 2

1

这是经典的递归,我们应用mapEithers到一个子列表产生一些类型的东西Either b [c],如果它是Left b,我们只是传播它。

如果是Right cs. 然后我们申请f到列表的头部。如果这产生错误,我们丢弃所有内容并向上传播,如果是Right c,那么结果是Right (c : cs)

而且因为我们需要一个递归的基本情况,所以一个空列表是Right [].

于 2013-11-10T14:48:18.843 回答
0

作为记录,这可以用fold. 在真正的 Haskell 代码中,您很少看到显式递归。相反,我们有一堆函数(以及其他fold, replicateM, map, filter)为我们做“繁重的工作”,我们只需要在使用它们时提供一些自定义它们的参数。

在您的情况下,您有两个Either值 - 列表其余部分的结果和当前值的结果。如果两者都是Right您希望它们一起出现在一个列表中,并且如果它们是Left您只想保留错误消息。

我在 Haskell 中描述的内容可以写成

(:) <$> f elem <*> rest

如果是 a或如果是 a ,这将返回Left值。如果两者都是,它将从中提取列表和值,并创建两者的新列表。f elemLeftrestLeftRightrestf elem

拨打fold电话,这意味着mapEithers在现实世界中更有可能写成

mapEithers f = foldr (\elem rest -> (:) <$> f elem <*> rest) (Right [])

编辑:我刚刚意识到通过一些额外的转换可以更简单。(虽然我的 Haskell-fu 不够强大,无法流利地阅读……)

mapEithers f = foldr (liftA2 (:) . f) (Right [])
于 2013-11-10T16:09:22.330 回答