2014-12-21 2 views
1

Я определил дерево:Чтение/запись переменных из/в дереве

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

type PersonTree = Tree PersonNode 

Мои вопросы, как я могу получить имя из моего узла и использовать его где-нибудь в другом месте. Лучше пример будет, если у меня есть Int Tree и я хотел бы добавить значения из дерева и добавить его к моему дереву, как показано выше:

Так функция будет:

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

Он принимает Ints из Data.Tree и должен добавить его в PersonNode в качестве возраста и берет информацию из списка и помещает ее в имя в этом случае. Моя проблема в том, что я не знаю, как получить информацию из Data.Tree и поместить ее в PersonTree в качестве конкретной переменной. Надеюсь, кто-то может мне помочь.

+0

Что здесь аргумент '[a]? Должно ли это быть '[String]' для представления имен? Является ли список имен и дерева веками лучшим способом для этого? – bheklilr

+0

Да, в этом случае это будет [String]. Список имен и дерева в течение многих лет может быть не оптимальным, но только для моего примера, когда я хочу прочитать информацию из одного дерева, но в другом. Эссенциально преобразует дерево в другое дерево. – user3657850

+0

Преобразование дерева довольно просто, но в каком порядке вы хотите назначить имена из списка до возраста в дереве? Вы могли бы сделать глубину - сначала или ширину - сначала или что - то еще полностью. – bheklilr

ответ

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") [] 
    ] 

Это можно выполнить несколько способов, прямая рекурсия бы быть одним из них, но это может стать сложным, потому что вам нужно полностью пересечь одну ветку, прежде чем идти дальше, не отставая от того, какие имена были назначены в первой ветке. Вместо этого мы можем использовать состояние монады, чтобы сделать это почти тривиальным, если несколько менее эффективен:

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 позволяет нам использовать очень простую рекурсию для создания дочерних узлов, а комбинаторы монад mapM и sequence делают это очень простым. Апликативные комбинаторы также делают обработку ошибок практически прозрачной, я никогда не упоминал Just или Nothing.

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) 

Примечания: он не работает на деревьях, которые не имеют одинакового размера. Если это проблема, вам нужно будет ее адаптировать, и я не тестировал ее. Надеюсь, что все-таки получится.

Редактировать: Кажется, я полностью не понял вопрос. Я думаю, однако, что приведенный выше пример полезен, демонстрируя, как деконструировать дерево.

+0

Спасибо, что помогли. Так что для стандартного Data.Tree мне нужно использовать age = (rootlabel Node)? – user3657850

+0

Нет, поскольку 'Node' не определен с использованием синтаксиса записи, который не будет работать (если вы не определите' rootlabel (Node lbl _) = lbl'). Вы должны ознакомиться с корневой меткой, используя [сопоставление с образцом] (http://learnyouahaskell.com/syntax-in-functions). @ user3657850 – 11684