3

这个想法是实现“惰性”长度函数来将列表长度与 Int 进行比较,而不计算整个长度。

{-# LANGUAGE DeriveFunctor
           , TypeFamilies
           , FlexibleInstances #-}
import Data.Functor.Foldable

type instance Base Int   = Maybe

现在我们可以有可折叠/不可折叠

instance Foldable Int where
  project 0 = Nothing
  project x = Just (x-1)

instance Unfoldable Int where
  embed Nothing  = 0
  embed (Just x) = x+1

我想将 [a] 转换为 Base Int Int:

leng :: [a] -> Base Int Int
leng = ana phi where
  phi :: [a] -> Base Int [a]
  phi []    = Nothing
  phi (_:t) = Just t

但这不起作用。它抱怨 [a] -> Base (Maybe Int) [a] 应该是 phi 的类型。我不明白为什么。

如果可行,那么我可以比较:

gt = curry $ hylo psi phi where
  phi (Just _, Nothing)  = Left True
  phi (Nothing, _)       = Left False
  phi (Just t, Just n)   = Right (t, n)

  psi (Left t)  = t
  psi (Right t) = t

main = print $ (leng [1..]) `gt` (ana project 4)

冷有什么问题?

4

4 回答 4

3

的类型ana(a -> Base t a) -> a -> t。请注意,它返回的是 plaint而不是Base t t。所以正确的类型leng

leng :: [a] -> Int
于 2013-08-28T13:36:25.050 回答
3

感谢您指出类型错误。获得正确的类型澄清了这个想法:)

{-# LANGUAGE DeriveFunctor
           , TypeFamilies
           , FlexibleInstances #-}
import Data.Functor.Foldable

type instance Base ([a], Int) = Either Bool

instance Foldable ([a], Int) where
  project ([], _) = Left False
  project (_, 0) = Left True
  project ((h:t), n) = Right (t, n-1)

longerThan :: [a] -> Int -> Bool
longerThan = curry $ cata $ either id id

main = print $ [1..] `longerThan` 4

使满意?让我们扩展它以说明我为什么真正开始这一切:

{-# LANGUAGE DeriveFunctor
           , TypeFamilies
           , FlexibleInstances
           , FlexibleContexts
           , UndecidableInstances #-}
import Data.Functor.Foldable

data Zip a b x = Z (Base a (Base b x))
instance (Functor (Base a), Functor (Base b)) => Functor (Zip a b) where
  fmap f (Z a) = Z $ fmap (\x -> fmap f x) a

type instance Base (a, b) = Zip a b

得到提示?我们可以同时递归这两个结构!

instance (Foldable a, Foldable b) => Foldable (a, b) where
  project (a, b) = Z $ fmap (\x -> fmap (\y -> (x,y)) $ project b) $ project a

演示:引入Base Int,并检查列表的长度是否大于给定的Int。

type instance Base Int = Maybe

instance Foldable Int where
  project 0 = Nothing
  project x = Just $ x-1

-- lt and gt are the same;
-- just showing off with the order of arguments, so you can appreciate Zip
lt :: Int -> [a] -> Bool
lt = curry $ cata phi where
  phi (Z Nothing) = True
  phi (Z (Just Nil)) = False
  phi (Z (Just (Cons _ t))) = t

gt :: [a] -> Int -> Bool
gt = curry $ cata phi where
  phi (Z (Cons _ Nothing)) = True
  phi (Z Nil) = False
  phi (Z (Cons _ (Just t))) = t

main = print [[1..] `gt` 4, 4 `lt` [1..]]
于 2013-08-28T15:09:13.343 回答
2

这可能会破坏使用类型族进行练习的目的,但如果您只是想要一个“懒惰地比较列表长度与 int”函数,您可以直接编写它:

cmp :: [a] -> Int -> Ordering
cmp [] n = compare 0 n
cmp (_:xs) n = if n <= 0 then GT else cmp xs (n - 1)
于 2013-08-28T13:55:49.857 回答
0

@PaulVisschers 正在实现的相同功能,这确实是做你想做的最简单的方法之一,可以用 catamorphism 来实现,如果由于某种原因你想用Foldables 进行练习。

import Data.Functor.Foldable

cmp :: [a] -> Int -> Ordering
cmp = cata psi

psi :: Base [a] (Int -> Ordering) -> Int -> Ordering
psi Nil n = compare 0 n
psi (Cons h t) n = if n <= 0 then GT else t (n-1)
于 2013-08-28T15:04:05.527 回答