Предупреждение: этот ответ содержит спойлеры.
Вы можете довольно легко написать оболочку вокруг существующей функции treeInsert
, которая позволит вам использовать do-notation так, как вы хотите. Согласно комментариям, есть функция modify
, которая принимает функцию изменения f :: s -> s
и превращает ее в State s ()
, которое является «действием» для изменения состояния s
. Это означает, что вы можете написать:
stateTreeInsert :: (Ord a) => a -> State (Tree a) ()
stateTreeInsert x = modify (treeInsert x)
или более кратко:
stateTreeInsert :: (Ord a) => a -> State (Tree a) ()
stateTreeInsert = modify . treeInsert
Затем вы можете определить «действие», например:
insertSomeStuff :: (Ord a, Num a) => State (Tree a) ()
insertSomeStuff = do
stateTreeInsert 0
stateTreeInsert 1
stateTreeInsert 2
, а затем примените его к конкретному дереву, используя execState
:
main = print $ execState insertSomeStuff EmptyTree
Однако, я думаю, вас больше интересовала повторная реализация treeInsert
с нуля в форме манипулирования состоянием.
Проблема в том, что «простой» способ сделать это не очень интересен или не вызывает нареканий c. Это просто неудобно. Это будет выглядеть примерно так:
awkwardTreeInsert :: (Ord a) => a -> State (Tree a) ()
awkwardTreeInsert x = do
t <- get
case t of
EmptyTree -> put $ Node x EmptyTree EmptyTree
Node a l r -> case compare x a of
LT -> do put l -- replace tree with left child
awkwardTreeInsert x -- insert into left child
l' <- get -- get the answer
put $ Node a l' r -- overwrite with whole tree w/ updated left child
GT -> do put r
awkwardTreeInsert x
r' <- get
put $ Node a l r'
EQ -> return ()
Проблема здесь в том, что состояние, как мы его написали, может одновременно содержать только одно дерево. Итак, если мы хотим вызвать алгоритм рекурсивно для вставки чего-либо в ветку, нам нужно перезаписать «большое дерево» одним из его дочерних элементов, запустить рекурсивную вставку, получить ответ и перезаписать его «большим деревом». с заменой соответствующего дочернего элемента.
В любом случае, он работает так же, как stateTreeInsert
, поэтому:
insertSomeStuff :: (Ord a, Num a) => State (Tree a) ()
insertSomeStuff = do
awkwardTreeInsert 0
awkwardTreeInsert 1
awkwardTreeInsert 2
main = print $ execState insertSomeStuff EmptyTree