略微概括展开

时间:2014-07-01 21:26:29

标签: haskell

Data.List定义

unfoldr :: (b -> Maybe (a, b)) -> b -> [a]
unfoldr f b = case f b of
  Just (a,new_b) -> a : unfoldr f new_b
  Nothing -> []

有许多函数几乎可以使用unfoldr定义,但在列表的最后有问题。一个简单的"修复"是

unfoldr' :: (b -> Either (a,b) [a]) -> b -> [a]
unfoldr' f b = case f b of
  Left (a, new_b) -> a : unfoldr' f new_b
  Right r         -> r

此功能是否具有标准名称?它是否具有良好的属性并与foldr良好互动?

2 个答案:

答案 0 :(得分:2)

与递归方案中的elgot类似,我认为。

扩展并再次查看,它可能只是来自同一个包的apo。该函数的类型为(Unfoldable t, Foldable t) => (a -> Base t (Either t a)) -> a -> t。将a重命名为b并将t改为[a],我们会(b -> Base [a] (Either [a] b)) -> b -> [a]

查看Base [a] (Either [a] b)部分需要引用源代码,但为我们提供了一个只有少数构造函数的数据系列:Cons a (Left [a])Cons a (Right b)Nil。现在,您的类型Either (a, b) [a]也只有少数构造函数:Left (a, b)Right (a:[a])Right []。我想你可以看到这两种类型之间存在同构。这是一方:

e2pe :: Either (a, b) [a] -> Prim [a] (Either [a] b)
e2pe (Left  (x,y)) = Cons x $ Right y
e2pe (Right (x:xs))  = Cons x $ Left xs
e2pe (Right [])    = Nil

我的(未经证实)声明是unfoldr' f = apo $ e2pe . f

在定义unfoldr'和我的e2pe

之后,进行了一些测试
GHCi> let f n = case compare n 0 of { EQ -> Right []; LT -> Left (n, succ n); GT -> Left (n, pred n); }
GHCi> unfoldr' f 5
[5,4,3,2,1]
GHCi> apo (e2pe . f) 5
[5,4,3,2,1]
GHCi> unfoldr' f (-5)
[-5,-4,-3,-2,-1]
GHCi> apo (e2pe . f) (-5)
[-5,-4,-3,-2,-1]
GHCi> let f n = case compare n 0 of { EQ -> Right [0]; LT -> Left (n, succ n); GT -> Left (n, pred n); }
GHCi> unfoldr' f 5
[5,4,3,2,1,0]
GHCi> unfoldr' f (-5)
[-5,-4,-3,-2,-1,0]
GHCi> apo (e2pe . f) 5
[5,4,3,2,1,0]
GHCi> apo (e2pe . f) (-5)
[-5,-4,-3,-2,-1,0]

如果我的主张是正确的,那么你已经彻底改变了这种同态。

答案 1 :(得分:1)

(这是一个评论,有些代码所以它不适合)考虑你在评论中提到的测试用例,

f xs -- = zip (map reverse $ inits xs) (tails xs)
  = unfoldr g (Just ([],xs))
      where
        g (Just (acc,xs@[]))    = Just ( (acc,xs), Nothing)
        g (Just (acc,xs@(x:t))) = Just ( (acc,xs), Just (x:acc, t) )
        g Nothing = Nothing

感知到的问题是对列表的一个额外处理节拍 end ,这迫使我们使用嵌套的Maybe。事实上,你的函数更容易(不需要嵌套的Maybe):

  = unfoldr' h ([],xs)
      where
        h (acc,xs@[])    = Right [ (acc,xs) ]    -- last tail
        h (acc,xs@(x:t)) = Left  ( (acc,xs), (x:acc,t) )

但我们也可以用另一种方式简化g代码:

  = unfoldr (fmap g') (Just ([],xs))
      where
        g' (acc,xs@[])    = ( (acc,xs), Nothing)   -- last element
        g' (acc,xs@(x:t)) = ( (acc,xs), Just (x:acc, t) )

并将其用作此类函数的框架,标准unfoldr仍然存在。或者,Nothing的定义中可能还有一行g代码,无需担心。

但当然,如果您真的需要在列表中添加特殊尾部,而不仅仅是一个元素,那么它就无法替代。