避免不完整的模式匹配

时间:2017-07-06 09:04:41

标签: list haskell pattern-matching refactoring

请考虑以下代码:

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 ()

这背后的想法是我得到了AB混合在一起的列表。我可以转换A -> B但不能相反。根据此列表,我想制作A的列表或B的列表,前者如果我的所有原始列表元素都是A s,后者如果至少一个是B

以上代码编译(我猜测会起作用)但是map (\(TA x) -> x) l中的不完整模式匹配让我有点不舒服。这种不完全匹配只是我在这里做的必要吗?另外,我是否重新发明轮子,是否有一些能够概括我在这里所做的事情?

3 个答案:

答案 0 :(得分:2)

我能想到的唯一方法就是

tryA :: [T] -> Maybe [A]
tryA [] = []
tryA (t:ts) =
  case t of
    TA x -> do xs <- tryA ts; return (x:xs)
    TB _ -> Nothing

如果tryA没有返回任何内容,请像以前一样map f' l

这样您只需一次通过all isA lmap,就可以避免不完整的模式。

答案 1 :(得分:2)

我按照这样的方式构建它:构建两个列表 - 一个充满A个和一个充满B个 - 这样可以构建A列表可能会失败。可以构建一个实现此逻辑的MonoidfoldMap

由于可能无法构建A的列表,因此我们需要在Monoid之上构建此Maybe。我们想要的行为来自Maybe&#39} Applicative个实例:如果mappend的任何一个参数都是Nothing,那么整个事情就会失败,否则我们希望使用mappend来合并这两个结果。这是组合ApplicativeMonoid的一般方法。具体地:

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

我不知道这个newtype是否在base的某个地方。似乎那种东西会存在,但我无法找到它。

不用多说,Monoid foldMap我们将type Result = ([B], WrappedApplicative Maybe [A]) ping到:

a

我借用了(a, b)'s Monoid instance,它与bMonoid 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 个实例并行委派。

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

actionBarDrawerToggle.setDrawerIndicatorEnabled(false);

看,妈,没有部分功能!

答案 2 :(得分:1)

在得到其他答案的一些帮助之后,我将回答我自己的问题,为未来的观众带来好处。我相信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 ()

在列表中,这应该只占用前导A的数量空间,这是我认为的最小值。