四叉树字符串的表示形式

问题描述

我得到了一个 ppeeefpffeefe 形式的字符串。 值:

  • p 表示父节点
  • e 表示空节点
  • f 表示完整节点

代表此字符串的图像可以在这里看到:https://i.stack.imgur.com/GZppc.png

我正在用Haskell编写代码,并尝试将这种表示形式转换为1024个长整数列表,其中1表示黑色(完整)像素,0表示白色(空白)像素(假设图像尺寸为32x32像素)。

这是我的代码,但是Haskell给我带来麻烦。我知道我需要跟踪访问过多少个父节点并以这种方式更新最高级别。我正在尝试采用DFS方式,但是任何可以做的工作都会有所帮助。

getQuad :: String -> Int -> Int -> Int -> [Int] -> [Int]
getQuad tree level highestLevel pCount result | (node == 'p') = result ++ (getQuad (drop 1 tree) (level+1) level 0 result)
                                              | (node == 'e')  = result ++ (getQuad (drop 1 tree) level highestLevel pCount (result ++ (take (getAmount level) [0,0..])))
                                              | (node == 'f') = result ++ (getQuad (drop 1 tree) level highestLevel pCount (result ++ (take (getAmount level) [1,1..])))
                                              | otherwise = result
                                               where
                                                    node = g

getNodeValue :: String -> Char
getNodeValue tree = if (length tree > 0) then tree !! 0 else 'x'

getAmount :: Int -> Int
getAmount l = 1024 `div` (4^l)

谢谢!

解决方法

我认为您试图在单个函数中做太多事情。我建议重新开始,并明确引入单独的解析阶段(将String转换为代表它的ADT)和生产阶段(将ADT的值转换为Int的列表)。例如,合适的ADT可能如下所示:

data QuadTree = Parent QuadTree QuadTree QuadTree QuadTree
              | Empty
              | Full
              deriving (Eq,Ord,Read,Show)

有多种分析技术和库。考虑到您明显的专业水平和格式的简单性,我认为我建议您从手工编写解析器开始,而忽略错误处理。稍后,您可以考虑学习有关MaybeEither之类的错误处理工具,并分析诸如parsec和朋友之类的组合器库,以使其更灵活地适应语言的更改。

因此,请手动忽略错误处理。这是我要放置并尝试填写的骨骼。我们的解析器不仅需要消耗String,还需要消耗String的一部分并说明剩余的内容:处理嵌套父节点时,我们需要返回到外部父节点内部父级不使用的字符串块。所以:

parseQuadTree :: String -> (String,QuadTree)
parseQuadTree ('p':rest) = -- TODO: exercise for the reader
parseQuadTree ('e':rest) = (rest,Empty)
parseQuadTree ('f':rest) = (rest,Full)
parseQuadTree other = error $ "parsing failed,expected a p,e,or f,but got " ++ other ++ " instead"

例如,一旦完成此功能,我们可能期望进行以下ghci交换:

> parseQuadTree "e"
("",Empty)
> parseQuadTree "eef"
("ef",Empty)
> parseQuadTree "peeeeef"
("ef",QuadTree Empty Empty Empty Empty)

一旦有了,我就会尝试对2d结果做出明智的表示。嵌套列表也许可以做到:

type Image = [[Int]]

例如,您可以将外部列表的每个元素解释为图像的一行;它的元素是该行的列。为此,您需要执行的三个基本操作是水平和垂直并排粘贴图像并创建空白图像。

hcat,vcat :: Image -> Image -> Image
hcat = -- TODO: exercise for the reader
vcat = -- TODO: exercise for the reader

blank :: Int -> Int -> Int -> Image
blank w h pixel = -- TODO: exercise for the reader
-- OR,you could take just one size argument; we only ever need
-- square blank images in the following code

例如,一旦我们完成了这些ghci交换,您可能会期望它们:

> :set +m
> let x = [[0,1]
|,[2,3]
|         ]
|     y = [[4,5]
|,[6,7]
|         ]
|
> hcat x y
[[0,1,4,5],3,6,7]]
> vcat x y
[[0,1],3],[4,7]]
> blank 2 3 4
[[4,4],4]]

现在,您可以编写将QuadTree转换为Image的函数。我们必须知道图像应该有多大,所以让我们对该函数进行参数设置。

renderQuadTree :: Int -> QuadTree -> Image
renderQuadTree size (Parent nw ne sw se) = -- TODO: exercise for the reader; use hcat and vcat
    where subtreeSize = size `div` 2
renderQuadTree size Empty = blank size size 0
renderQuadTree size Full = blank size size 1

例如,一旦完成,我们可能会期望在ghci进行此类交流:

> renderQuadTree 2 Empty
[[0,0],[0,0]]
> renderQuadTree 2 Full
[[1,[1,1]]
> renderQuadTree 2 (Parent Empty Full Full Empty)
[[0,0]]
> renderQuadTree 4 (Parent Empty (Parent Full Empty Empty Full) Empty Full)
[[0,1]]

最后,我们可以创建一个顶层功能,将所有这些功能组合到一个方便的部分中。

getQuad :: String -> [Int]
getQuad s = case parseQuadTree s of
    ("",t) -> concat (renderQuadTree 32 t)
    (s',_) -> error $ "parser did not consume the entire description string,leftovers are: " ++ s