我定义了一棵树:
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。 希望有人可以帮助我。
答案 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组合器mapM
和sequence
使这非常简单。应用程序组合器也使错误处理几乎透明,我从不必提及Just
或Nothing
。
答案 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)
注意:它在不同大小的树上失败。如果这是一个问题,你需要调整它,我没有测试它。我希望它可以解决这个问题。
编辑:似乎我完全误解了这个问题。不过,我认为上面的例子有助于演示如何解构树。