【问题标题】:Read/write variable from/to tree从/到树读/写变量
【发布时间】:2014-12-21 15:59:05
【问题描述】:

我定义了一棵树:

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。 希望有人可以帮助我。

【问题讨论】:

  • 这里的[a] 参数是什么?应该改为[String] 来表示名称吗?一个名字列表和一个年龄树是解决这个问题的最好方法吗?
  • 是的,在这种情况下它应该是一个 [String]。名称列表和年龄树可能不是最佳选择,但这仅适用于我的示例,我想从一棵树中读取信息,但将其读取到另一棵树中。基本上将一棵树变成另一棵树。
  • 树形转换非常简单,但是您希望将列表中的名称分配给树中的年龄的顺序是什么?你可以做深度优先或广度优先,或者完全其他的事情。
  • 列表是随机的,所以没那么重要。它只是让我可以理解这个过程以及如何做这样的事情。如果这很重要,我喜欢使用深度优先。

标签: haskell tree nodes


【解决方案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") []
    ]

这可以通过多种方式完成,直接递归就是其中之一,但这可能会变得很棘手,因为您需要在下一个分支之前完全遍历一个分支,同时跟上第一个分支上分配的名称。相反,我们可以使用 state 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 使这变得非常简单。 applicative 组合器还使错误处理几乎透明,我从来不用提及JustNothing

【讨论】:

    【解决方案2】:

    模式匹配与递归相结合!

    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)
    

    注意:它在大小不同的树上失败。如果这是一个问题,你需要调整它,我没有测试它。不过,我希望它能传达这个想法。

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

    【讨论】:

    • 感谢您的帮助。所以对于标准 Data.Tree 我必须使用 age = (rootlabel Node)?
    • 不,因为Node 没有使用无法工作的记录语法定义(除非您定义rootlabel (Node lbl _) = lbl)。您应该使用pattern matching 获取根标签。 @user3657850
    猜你喜欢
    • 2018-08-31
    • 2015-12-13
    • 1970-01-01
    • 1970-01-01
    • 2016-06-13
    • 2021-12-26
    • 2020-10-14
    • 1970-01-01
    • 2017-08-12
    相关资源
    最近更新 更多