6

Control.Applicative.optional 允许处理零个或一个 Applicatives。many & some 分别允许 0 或更多,或 1 或更多。我想创建一个专门处理零、一或二的函数。签名可能与许多/一些一样,即

zeroOneOrTwo :: Alternative f => f a -> f [a]

我觉得这应该很简单,但我已经玩了一段时间了,无法让它工作。任何指针将不胜感激。

4

2 回答 2

8

这个怎么样:

zeroOneOrTwo :: Alternative f => f a -> f [a]
zeroOneOrTwo a = go (2 :: Int)
  where
    go n
      | n > 0 = ((:) <$> a <*> go (n - 1)) <|> pure []
      | otherwise = pure []
于 2016-09-25T19:49:14.533 回答
5

如果你要麻烦限制这样的结果,你不妨让它的类型反映这一点。

data ZOT a = Zero | One a | Two a a

form :: a -> Maybe a -> ZOT a
form a Nothing = One a
form a (Just b) = Two a b

zeroOneOrTwo :: Alternative f => f a -> f (ZOT a)
zeroOneOrTwo a = (form <$> a <*> optional a) <|> pure Zero

如果你想要最多三个怎么办?还是最多四个?您可以使用几个语言扩展同时涵盖所有此类情况。

{-# LANGUAGE DataKinds, GADTs #-}

data Nat = Z | S Nat

data Natty n where
  Zy :: Natty 'Z
  Sy :: Natty n -> Natty ('S n)

data AtMost n a where
  Nil :: AtMost n a
  Cons :: a -> AtMost n a -> AtMost ('S n) a

atMost :: Alternative f => Natty n -> f a -> f (AtMost n a)
atMost Zy _ = pure Nil
atMost (Sy n) a = (Cons <$> a <*> atMost n a) <|> pure Nil

如果您不想使用任何花哨的扩展怎么办?好吧,它看起来不会那么漂亮,但是如果您愿意,您仍然可以这样做,从 Ralf Hinze 的“作为高阶嵌套数据类型的数值表示”中获取一页。

data Z a = Z deriving (Show)
data S f a = Nil | Cons a (f a) deriving (Show)

class AtMost g where
  atMost :: Alternative f => f a -> f (g a)

instance AtMost Z where
  atMost _ = pure Z
instance AtMost g => (AtMost (S g)) where
  atMost m = (Cons <$> m <*> atMost m) <|> pure Nil

请注意,现在有两种不同的方法来构造空结果,ZNil,具有不同的类型。Z当结果与请求一样大时Nil使用,而当结果不足时使用。

*AtMost> atMost (Just 3) :: Maybe ((S (S (S Z))) Int)
Just (Cons 3 (Cons 3 (Cons 3 Z)))

*AtMost> atMost Nothing :: Maybe ((S (S (S Z))) Int)
Just Nil

*AtMost> atMost undefined :: Maybe (Z Int)
Just Z
于 2016-09-25T20:51:03.733 回答