[英]Avoiding an incomplete pattern match
Consider the following code: 考虑以下代码:
data A
data B
f :: A -> B
f = undefined
data T = TA A | TB B
data ListT = ListTA [A] | ListTB [B]
g :: [T] -> ListT
g l =
let
f' :: T -> B
f' (TA x) = f x
f' (TB x) = x
isA :: T -> Bool
isA TA{} = True
isA TB{} = False
in
case (all isA l) of
True -> ListTA (map (\(TA x) -> x) l)
False -> ListTB (map f' l)
main = pure ()
The idea behind this is I've got a list of either A
s or B
s mixed together. 这背后的想法是我得到了
A
或B
混合在一起的列表。 I can convert A -> B
but not the other way around. 我可以转换
A -> B
但不能反过来。 Based on this list, I want to make either a list of A
s or list of B
s, the former if all my original list elements are A
s, the latter if at least one is a B
. 基于此列表,我想创建一个
A
列表或B
列表,如果我所有原始列表元素都是A
,则前者,如果至少一个是B
,则后者。
The above code compiles (and I'm guessing will work) but the incomplete pattern match in the map (\\(TA x) -> x) l
makes me just a little uncomfortable. 上面的代码可以编译(并且我猜想会起作用),但是
map (\\(TA x) -> x) l
不完整的模式匹配map (\\(TA x) -> x) l
使我有点不舒服。 Is such an incomplete match just a necessity of what I'm doing here? 如此不完整的匹配仅仅是我在这里所做的必要吗? Also, am I reinventing the wheel, is there something that generalises what I'm doing here?
另外,我是否正在重新发明轮子,是否可以概括一下我在这里所做的事情?
The only way I can think of is something like 我能想到的唯一方法是
tryA :: [T] -> Maybe [A]
tryA [] = []
tryA (t:ts) =
case t of
TA x -> do xs <- tryA ts; return (x:xs)
TB _ -> Nothing
If tryA
returns nothing, then do map f' l
as before. 如果
tryA
返回任何内容,则像以前一样map f' l
tryA
。
This way you're doing the all isA l
and the map
in a single pass, and it avoids an incomplete pattern. 这样,您可以一次完成
all isA l
和map
,并且避免了不完整的模式。
I'd structure it like this: build two lists - one full of A
s and one full of B
s - with the effect that building the list of A
s could fail. 我会这样构造:建立两个列表-一个完整的
A
列表和一个完整的B
列表-结果是建立A
列表可能会失败。 One can build a Monoid
which implements this logic and foldMap
into it. 可以构建一个
Monoid
来实现此逻辑并将foldMap
嵌入其中。
Since one could fail to build a list of A
s, we'll need to build this Monoid
on top of Maybe
. 由于可能无法建立
A
的列表,因此我们需要在Maybe
顶部建立此Monoid
。 The behaviour we want comes from Maybe
's Applicative
instance: if either of mappend
's arguments is Nothing
then the whole thing fails, otherwise we want to use mappend
to combine the two results. 我们想要的行为来自
Maybe
的Applicative
实例:如果mappend
的参数之一为Nothing
那么整个操作都会失败,否则,我们想使用mappend
来组合这两个结果。 This is a general recipe for combining an Applicative
and a Monoid
. 这是组合
Applicative
和Monoid
的一般方法。 Concretely: 具体来说:
newtype WrappedApplicative f a = Wrap { unWrap :: f a }
instance (Applicative f, Monoid m) => Monoid (WrappedApplicative f m) where
mempty = pure mempty
Wrap x `mappend` Wrap y = Wrap $ liftA2 mappend x y
I don't know if this newtype
is somewhere in base
. 我不知道这
newtype
在某处base
。 It seems like the sort of thing that would be there but I couldn't find it. 似乎会有东西出现,但我找不到。
Without further ado, here's the Monoid
we'll be foldMap
ping into: foldMap
,这是Monoid
我们将foldMap
ping到:
type Result = ([B], WrappedApplicative Maybe [A])
I'm borrowing (a, b)
's Monoid
instance , which delegates in parallel to a
and b
's Monoid
instances. 我要借用
(a, b)
的Monoid
实例 ,该实例并行地委托给a
和b
的Monoid
实例。
getAsOrToBs :: [Either A B] -> Either [A] [B]
getAsOrToBs = fromResult . foldMap toResult
where toResult (Left a) = ([aToB a], Wrap (Just [a]))
toResult (Right b) = ([b], Wrap Nothing)
fromResult (_, Wrap (Just as)) = Left as
fromResult (bs, Wrap Nothing) = Right bs
Alternatively, with foldr
: 或者,使用文件
foldr
:
getAsOrToBs :: [Either A B] -> Either [A] [B]
getAsOrToBs = fromResult . foldr f ([], Just [])
where f (Left a) (bs, mas) = (aToB a : bs, fmap (a:) mas)
f (Right b) (bs, _) = (b:bs, Nothing)
fromResult (_, Just as) = Left as
fromResult (bs, Nothing) = Right bs
Look, ma, no partial functions! 瞧,妈,没有局部功能!
After some help from the other answers, I'm going to answer my own question for the benefit of future viewers. 在获得其他答案的帮助之后,我将回答自己的问题,以使将来的观众受益。 I believe the most suscinct function for
g
is as follows (and noticed I've generalised to Traversable
instead of just lists). 我相信
g
的最明显的功能如下(并注意到我已将其推广到Traversable
而不是列表)。
data ListT t = ListTA (t A) | ListTB (t B)
g :: (Traversable t) => t T -> ListT t
g l =
let
f2B :: T -> B
f2B (TA x) = f x
f2B (TB x) = x
f2A :: T -> Maybe A
f2A (TA x) = Just x
f2A (TB x) = Nothing
in
maybe (ListTB (fmap f2B l)) ListTA (traverse f2A l)
main = pure ()
On lists, this should only take space proportional to the number of leading A
s, which is minimal I believe. 在列表上,此空间只应与前导
A
的数量成比例,我认为这是最小的。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.