我正在尝试在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值。
答案 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) []]]
结果与维基百科上的示例结果不同,但仅在索引值中由于树的不同(尽管有效)遍历而存在。