在haskell中实现嵌套集

时间:2014-08-16 14:53:44

标签: haskell

我正在尝试在Haskell中实现嵌套的set模式。我有一组用户安排在树上。它们的定义如下:

data User = User {u_id :: Int, parent :: Int, lft :: Int, rgt :: Int} deriving (Show)
data Tree a = EmptyTree | Node a [Tree a] deriving (Show, Eq)

u_id和parent的数据来自数据库,然后我必须从中计算lft和rgt值。到目前为止,这是我不完整的功能:

calcNestedSet :: Tree User -> Tree User
calcNestedSet EmptyTree = EmptyTree
calcNestedSet tree = calcNestedSet' 1 $ resetTree tree
    where
        setLftandRgt u l r = User (u_id u) (parent u) l r
        setLft u l = setLftandRgt u l (rgt u)
        setRgt u r = setLftandRgt u (lft u) r
        resetTree (Node a children) = Node (setLftandRgt a 0 0) $ map resetTree children
        -- If at a leaf, set both the left and the right and return the node
        calcNestedSet' counter (Node a []) = Node (setLftandRgt a counter (counter+1)) []
        -- If at a branch, set the left value and traverse to the next eligible child
        calcNestedSet' counter (Node a children) = Node (setLft a counter) $ map (calcNestedSet' (counter+1)) children

但是,我无法在没有能力看到“备份”的情况下解决这个问题。树看到父lft和rgt值。

2 个答案:

答案 0 :(得分:3)

现在你只是在推动柜台"向下"那个树。您还需要在它回来时捕获它" up"

-- For simplicity, we don't really need EmptyTree, assuming we 
-- only care about values stored in the tree
data Tree  a = Node  a [Tree a] deriving Show

-- Universal nested set label, don't need to associate it with 
-- the data in the tree
data Label a = Label a Int Int 

nest' :: Tree a           -- undecorated tree
      -> Int              -- state being passed down
      -> ( Tree (Label a) -- decorated tree
         , Int            -- state being passed up
         )

nest' (Node a []) n = (Node (Label a n (n+1)) [], n+2)

如果有点奇怪的话,以上所有都应该是相当明显的。重点是我要连接一个新的"返回柜台"到nest'的结果,它将计数器传回" up"那个树。我们目前处理的是#34;没有孩子" case,我们可以通过子列表来扩展它以包含子项。

nest' (Node a cs) dn =
  let (cslabeled, up) = mapNest' cs (dn+1)
  in  (Node (Label a dn up), up+1)

mapNest' []     dn = ([], dn)
mapNest' (c:cs) dn =
  let (c,  mid) = nest' c dn
      (cs, up ) = mapNest' cs mid
  in (c:cs, up)

这个逻辑有点难以理解,但它本质上只是在我们向下,向上和穿过树时传递节点计数器的问题。幸运的是,我们可以通过注意到Int -> (Tree (Label a), Int)只是State Int (Tree (Label a))来实现这一点,因此

nest' :: Tree a -> State Int (Tree (Label a))

另一个重大改进是注意nest'中的一些重复功能,因为nest'mapNest'都处理空子列表方案。总之,我们可以非常简单地编写monadic nest,只关注主逻辑

nest' (Node a cs) = do
  d <- get
  let (csl, u) = runState (mapM nest cs) (d+1)
  put (u+1)
  return (Node (Label a d u) csl)

然后,在我们的顶级API中运行生成的State monad

nest :: Tree a -> Tree (Label a)
nest t = evalState (nest' t) 0

这样

>>> x = Node "hi" [Node "bye" [], Node "go" []]
>>> nest x
Node (Label "hi" 0 5) [Node (Label "bye" 1 2) [],Node (Label "go" 3 4) []]

答案 1 :(得分:1)

这可以通过使用左值和将nestedSet应用于给定元素的子元素的结果来实现,以便获得正确的值。

考虑以下解决方案

data User a = User {u_id :: a, parent :: a, lft :: Int, rgt :: Int} deriving (Show)
data Tree a = EmptyTree | Node a [Tree a] deriving (Show, Eq)

nestedSet :: Tree (User a) -> Tree (User a)
nestedSet = snd . nestedSetHelper 1
  where
    nestedSetHelper:: Int -> Tree (User a) -> (Int, Tree (User a))
    nestedSetHelper l EmptyTree = (l, EmptyTree)
    nestedSetHelper l (Node (User {u_id=uid, parent=par}) children) = 
      ((r+1), Node (User uid par l r) new_trees)
      where
        r :: Int
        (r, new_trees) = calcNestedSet (l+1) children
        calcNestedSet :: Int ->
                         [Tree (User a)] ->
                         (Int, [Tree (User a)])
        calcNestedSet leftValue trees =
          foldr func (leftValue, []) trees
          where
            func :: Tree (User a) ->
                    (Int, [Tree (User a)]) ->
                    (Int, [Tree (User a)])
            func t (v, ts) = (new_v, new_t:ts)
              where
                (new_v, new_t) = nestedSetHelper v t

nestedSetHelper使用l的给定值,并根据计算子项为r创建一个值。

注意,我更改了User的类型,因此我可以使用维基百科页面示例http://en.wikipedia.org/wiki/Nested_set_model#Example上的值对此解决方案进行测试,并且更容易阅读结果。

我创造了这个测试。

test :: Tree (User String)
test = Node (User "Clothing" "" 0 0)
       [Node (User "Men's" "Clothing" 0 0)
        [Node (User "Suits" "Men's" 0 0 )
         [Node (User "Slacks" "Suits" 0 0) []],
         Node (User "Jackets" "Suits" 0 0) []],
        Node (User "Women's" "Clothing" 0 0)
        [Node (User "Dresses" "Women's" 0 0)
         [Node (User "Evening Gowns" "Dresses" 0 0) [],
          Node (User "Sun Dresses" "Dresses" 0 0) []],
         Node (User "Skirts" "Women's" 0 0) [],
         Node (User "Blouses" "Women's" 0 0) []]]

结果与维基百科上的示例结果不同,但仅在索引值中由于树的不同(尽管有效)遍历而存在。