Таким образом, для простого примера, если бы мы имели что-то вроде
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
.
Что здесь аргумент '[a]? Должно ли это быть '[String]' для представления имен? Является ли список имен и дерева веками лучшим способом для этого? – bheklilr
Да, в этом случае это будет [String]. Список имен и дерева в течение многих лет может быть не оптимальным, но только для моего примера, когда я хочу прочитать информацию из одного дерева, но в другом. Эссенциально преобразует дерево в другое дерево. – user3657850
Преобразование дерева довольно просто, но в каком порядке вы хотите назначить имена из списка до возраста в дереве? Вы могли бы сделать глубину - сначала или ширину - сначала или что - то еще полностью. – bheklilr