【问题标题】:Slightly generalizing unfold稍微概括展开
【发布时间】:2014-08-22 13:26:51
【问题描述】:

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 很好地交互?

【问题讨论】:

  • 有什么例子可以说明用unfoldr 很难定义但用unfoldr' 很容易定义的函数?
  • @amalloy,考虑f xs = zip (map reverse $ inits xs) (tails xs)
  • 如果bEither,则可以根据unfold 实现unfoldr'
  • @GabrielGonzalez,这是一个很好的观点。你认为它会同样有效吗?
  • 它让我想起了一些我称之为generalFold的东西:pastebin.com/r7jdBVDM(这不是我的粘贴箱,但我认为这是我的generalFold的代码现在唯一在线的地方)。 LeftRight 与您必须利用 Either monad 的内容相反,它也更类似于在典型的 unfoldr 中使用 Maybe 的方式(NothingLeft ...传统上表示失败,因此它们对应于终止)。

标签: haskell


【解决方案1】:

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

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

感知到的问题是结束列表的一个额外处理节拍,这迫使我们使用嵌套的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。或者,g 定义中的另一行 Nothing 代码可能没什么好担心的。

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

【讨论】:

    【解决方案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]
    

    如果我的说法是正确的,那么你已经重新发明了同形性。

    【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-09
    • 1970-01-01
    • 2013-05-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多