我正在阅读this paper by Chris Okasaki;标题为“广度优先编号:算法设计中小练习的教训”。
问题是 - 算法中的魔法是怎么发生的?有一些数字(例如图7标题为“将一个级别的输出线程化为下一级别的输入”) 不幸的是,也许只有我,但这个数字让我感到困惑。我不明白线程是如何发生的?
答案 0 :(得分:3)
广度优先遍历意味着逐个遍历树的级别。因此,我们假设我们已经知道每个级别开头的数字是什么 - 到目前为止每个级别之前的遍历元素的数量。对于论文中的简单例子
import Data.Monoid
data Tree a = Tree (Tree a) a (Tree a)
| Empty
deriving (Show)
example :: Tree Char
example = Tree (Tree Empty 'b' (Tree Empty 'c' Empty)) 'a' (Tree Empty 'd' Empty)
大小将为0,1,3,4。知道了这一点,我们可以通过给定树(子树)从左到右来编写这样的大小列表:我们通过以下方式推进列表的第一个元素:一个用于节点,并首先在列表的尾部穿过左侧,然后穿过右侧子树(请参阅下面的thread
)。
在这样做之后,我们将再次获得相同的大小列表,仅移动一个 - 现在我们在每个级别之后有的元素总数。所以诀窍是:假设我们有这样一个列表,用它来计算,然后输出输出作为输入 - tie the knot。
示例实施:
tagBfs :: (Monoid m) => (a -> m) -> Tree a -> Tree m
tagBfs f t = let (ms, r) = thread (mempty : ms) t
in r
where
thread ms Empty = (ms, Empty)
thread (m : ms) (Tree l x r) =
let (ms1, l') = thread ms l
(ms2, r') = thread ms1 r
in ((m <> f x) : ms2, Tree l' m r')
概括为Monoid
(对于编号,您将const $ Sum 1
作为函数)。
答案 1 :(得分:3)
查看树编号的一种方法是遍历。具体来说,我们希望使用State
以广度优先顺序遍历树以进行计数。必要的Traversable
实例看起来像这样。请注意,您可能实际上想要为newtype
BFTree
定义此实例,但为了简单起见,我只是使用原始Tree
类型。此代码受到Cirdec's monadic rose tree unfolding code中的想法的强烈启发,但此处的情况似乎要简单得多。希望我没有错过任何可怕的东西。
{-# LANGUAGE DeriveFunctor,
GeneralizedNewtypeDeriving,
LambdaCase #-}
{-# OPTIONS_GHC -Wall #-}
module BFT where
import Control.Applicative
import Data.Foldable
import Data.Traversable
import Prelude hiding (foldr)
data Tree a = Tree (Tree a) a (Tree a)
| Empty
deriving (Show, Functor)
newtype Forest a = Forest {getForest :: [Tree a]}
deriving (Functor)
instance Foldable Forest where
foldMap = foldMapDefault
-- Given a forest, produce the forest consisting
-- of the children of the root nodes of non-empty
-- trees.
children :: Forest a -> Forest a
children (Forest xs) = Forest $ foldr go [] xs
where
go Empty c = c
go (Tree l _a r) c = l : r : c
-- Given a forest, produce a list of the root nodes
-- of the elements, with `Nothing` values in place of
-- empty trees.
parents :: Forest a -> [Maybe a]
parents (Forest xs) = foldr go [] xs
where
go Empty c = Nothing : c
go (Tree _l a _r) c = Just a : c
-- Given a list of values (mixed with blanks) and
-- a list of trees, attach the values to pairs of
-- trees to build trees; turn the blanks into `Empty`
-- trees.
zipForest :: [Maybe a] -> Forest a -> [Tree a]
zipForest [] _ts = []
zipForest (Nothing : ps) ts = Empty : zipForest ps ts
zipForest (Just p : ps) (Forest ~(t1 : ~(t2 : ts'))) =
Tree t1 p t2 : zipForest ps (Forest ts')
instance Traversable Forest where
-- Traversing an empty container always gets you
-- an empty one.
traverse _f (Forest []) = pure (Forest [])
-- First, traverse the parents. The `traverse.traverse`
-- gets us into the `Maybe`s. Then traverse the
-- children. Finally, zip them together, and turn the
-- result into a `Forest`. If the `Applicative` in play
-- is lazy enough, like lazy `State`, I believe
-- we avoid the double traversal Okasaki mentions as
-- a problem for strict implementations.
traverse f xs = (Forest .) . zipForest <$>
(traverse.traverse) f (parents xs) <*>
traverse f (children xs)
instance Foldable Tree where
foldMap = foldMapDefault
instance Traversable Tree where
traverse f t =
(\case {(Forest [r]) -> r;
_ -> error "Whoops!"}) <$>
traverse f (Forest [t])
现在我们可以编写代码来将树的每个元素与其广度优先的数字配对,如下所示:
import Control.Monad.Trans.State.Lazy
numberTree :: Tree a -> Tree (Int, a)
numberTree tr = flip evalState 1 $ for tr $ \x ->
do
v <- get
put $! (v+1)
return (v,x)