用于平衡二叉树根的单通透镜

时间:2016-03-24 16:58:32

标签: haskell lens gadt

我有一个平衡的二叉树,其中包含树的深度类型:

data Nat = Zero | Succ Nat
data Tree (n :: Nat) a where
  Leaf :: Tree Zero a
  Branch :: a -> (Tree n a, Tree n a) -> Tree (Succ n) a

我希望在任意f :: Tree n a -> Tree n a的根,[{1}}≥{{1}的根深度n子树上运行任意函数Tree m a }}

我能够使用类型类实现这种方式来提取和替换根子树:

m

虽然这有效,但它需要两次遍历根子树,如果可能的话,我想在一个中完成。

通过使用延迟评估(打结),几乎可能

n

但是,如果你真的尝试运行mapRoot :: X m n => (Tree n a -> Tree n a) -> Tree m a -> Tree m a mapRoot f t = putRoot (f (getRoot t)) t class X m n where getRoot :: Tree m a -> Tree n a putRoot :: Tree n a -> Tree m a -> Tree m a instance X m Zero where getRoot t = Leaf putRoot Leaf t = t instance X m n => X (Succ m) (Succ n) where getRoot (Branch a (l,r)) = (Branch a (getRoot l, getRoot r)) putRoot (Branch a (l,r)) (Branch _ (l',r')) = Branch a (putRoot l l', putRoot r r') ,你会发现它并没有停止;这是因为mapRoot' :: Y m n => (Tree n a -> Tree n a) -> Tree m a -> Tree m a mapRoot' f t = t' where (r, t') = swapRoot t r' r' = f r class Y m n where swapRoot :: (Tree m a, Tree n a) -> (Tree n a, Tree m a) instance Y m Zero where swapRoot t leaf = (leaf, t) instance Y m n => Y (Succ m) (Succ n) where swapRoot (Branch a (l,r)) (Branch a' (l',r')) = (Branch a (lx,rx), Branch a' (lx',rx')) where (lx,lx') = swapRoot l l' (rx,rx') = swapRoot r r' 在第二个参数中并不是懒惰的(它不可能,因为mapRoot'是GADT)。

但是,鉴于swapRootTree n a,我已经为根子树提供了 镜头,这让我怀疑其他人,包括一个可用于在一次通过中实现getRoot的文件。

什么是这样的镜头?

1 个答案:

答案 0 :(得分:3)

你的打结"方法是合理的 - 你只需要在正确的位置使用所有参数,这样功能就足够懒惰了。

data (:<=) (n :: Nat) (m :: Nat) where 
  LTEQ_0 :: 'Zero :<= n 
  LTEQ_Succ :: !(n :<= m) -> 'Succ n :<= 'Succ m

mapRoot :: n :<= m -> (Tree n a -> Tree n a) -> Tree m a -> Tree m a 
mapRoot p0 f0 t0 = restore (f0 root) where 
  (root, restore) = go p0 t0 

  go :: n :<= m -> Tree m a -> (Tree n a, Tree n a -> Tree m a) 
  go LTEQ_0 t = (Leaf, const t) 
  go (LTEQ_Succ p) (Branch a (l,r)) = 
    case (go p l, go p r) of 
      ((l', fl), (r', fr)) -> 
        ( Branch a (l', r')
        , \(Branch a1 (l1, r1)) -> Branch a1 (fl l1, fr r1)
        )

注意go返回一对 - 根树,以及一个获取已处理根并返回结果的函数。这使得(对程序员和运行时!)明确表示结果Tree n a不依赖于输入Tree n a

另外,为了简洁起见,我用GADT取代了你的班级。

相关问题