从/向树读/写变量

时间:2014-12-21 15:59:06

标签: haskell tree nodes

我定义了一棵树:

data PersonNode = PersonNode 
    { age :: Int 
     , name :: String } 
deriving (Ord,Eq,Show,Read)

type PersonTree = Tree PersonNode

我的问题是如何从我的节点获取名称并在其他地方使用它。一个更好的例子是如果我有一个Int树,我会从树中添加值并将其添加到我的树中,如上所示:

所以函数将是:

import Data.Tree
f: [a] -> [Tree Int] -> [PersonTree]

从Data.Tree获取Ints并将其作为年龄添加到PersonNode,并从列表中获取信息并在此情况下将其放入名称中。 我的问题是我不知道如何从Data.Tree获取信息并将其作为特定变量放入PersonTree。 希望有人可以帮助我。

2 个答案:

答案 0 :(得分:1)

所以对于一个简单的例子,如果我们有像

这样的东西
names = ["A", "B", "C", "D", "E", "F", "G"]
ageTree =
    Node 1 [
        Node 2 [
            Node 3 []
            ],
        Node 4 [
            Node 5 [],
            Node 6 []
            ],
        Node 7 []
        ]

然后我们希望buildPersonTree names ageTree输出类似

的内容
Node (Person 1 "A") [
    Node (Person 2 "B") [
        Node (Person 3 "C") []
        ],
    Node (Person 4 "D") [
        Node (Person 5 "E") [],
        Node (Person 6 "F") []
        ],
    Node (Person 7 "G") []
    ]

这可以通过多种方式实现,直接递归将成为其中之一,但这可能会变得棘手,因为您需要在下一个分支之前完全遍历一个分支,同时保持在第一个分支上分配的名称。相反,我们可以使用状态monad使这几乎变得微不足道,如果效率稍低:

import Data.Tree
import Data.Maybe (listToMaybe)
import Control.Applicative
import Control.Monad.State


data PersonNode = PersonNode { age :: Int, name :: String } deriving (Eq, Show)


mkPerson :: (Functor m, MonadState [String] m) => Int -> m (Maybe PersonNode)
mkPerson age' = do
    -- name' :: Maybe String
    name' <- listToMaybe <$> get
    -- Remove that name from the head of the list of names
    modify (drop 1)
    -- fmap PersonNode over our Maybe String in name'
    return $ PersonNode age' <$> name'

buildTree :: (Functor m, MonadState [String] m) => Tree Int -> m (Maybe (Tree PersonNode))
buildTree (Node age' children) = do
    -- Get the root PersonNode using mkPerson
    root <- mkPerson age'
    -- children' :: [Maybe (Tree PersonNode)]
    children' <- mapM buildTree children
    -- Applicative combinators make error handling simple
    return $ Node <$> root <*> sequence children'

main :: IO ()
main = putStrLn
     $ maybe "Not enough names" (drawTree . fmap show)
     $ evalState (buildTree testAgeTree) testNames

testAgeTree :: Tree Int
testAgeTree = Node 1 [Node 2 [Node 3 []], Node 4 [Node 5 [], Node 6 []], Node 7 []]

testNames :: [String]
testNames = ["A", "B", "C", "D", "E", "F", "G"]

我确保使用Maybe表示无法从列表中获取名称,这样会使事情变得更复杂,但除了Haskell允许我们使用非常简单的递归来构建子项节点和monad组合器mapMsequence使这非常简单。应用程序组合器也使错误处理几乎透明,我从不必提及JustNothing

答案 1 :(得分:0)

模式匹配与递归结合起来做到了!

addAge :: Tree Int -> Tree PersonNode -> Tree PersonNode
addAge (Node addTreeRoot []) (Node personsRoot []) = (Node (PersonNode { age = addTreeRoot + (age personsRoot), name = (name personsRoot) }) [])
addAge (Node addTreeRoot addTreeForest) (Node personsRoot personsForest) = Node (PersonNode { age = addTreeRoot + (age personsRoot), name = (name personsRoot) } (addAge addTreeForest personsForest)

注意:它在不同大小的树上失败。如果这是一个问题,你需要调整它,我没有测试它。我希望它可以解决这个问题。

编辑:似乎我完全误解了这个问题。不过,我认为上面的例子有助于演示如何解构树。