Подтвердить что ты не робот

Если вы не можете изменить значение переменной в Haskell, как вы создаете структуры данных?

В соответствии с названием.

У меня есть следующий код, который создает двоичное дерево поиска, но если я хочу, чтобы он был создан и динамически изменен с использованием пользовательского ввода, как бы это сделать, если я не могу изменить значение переменной в haskell?!?

find :: (Ord a) => Node a -> a -> Bool
find (Node val left right) s
    | s == val      = True
    | s < val       = find left s
    | s > val       = find right s

find Empty s = False

data Node a = Node a (Node a) (Node a)
              | Empty

myTree = Node "m"   (Node "a" Empty Empty)
                    (Node "z" Empty Empty)

Спасибо заранее!

4b9b3361

Ответ 1

Идея чисто функциональных структур данных состоит в том, чтобы вычислять новые значения вместо их изменения и передавать их (рекурсивно) в параметры, а не хранить их глобально.

Итак, заданная функция

insert :: Ord a => Node a -> a -> Node a

ваша программа может выглядеть так:

-- Let the user enter k values that are stored in a tree structure
addTreeItems :: Int -> Node Int -> IO (Node Int)
addTreeItems 0 tree = return tree
addTreeItems k tree = do
    putStr "Enter new item: "
    item <- readLn
    addTreeItems (k - 1) (insert tree item) -- Recursively pass the tree

main = do
    tree <- addTreeItems 10 Empty
    -- ...

Используя монадические вспомогательные функции, это может быть упрощено для таких вещей, как

(foldl insert Empty) `liftM` (sequence $ replicate k (putStr "Enter new item: " >> readLn))

Если вы хотите обновить значения в определенной позиции, вам понадобятся более продвинутые структуры данных, такие как zipper, которые тем не менее остаются чисто функциональный!

Ответ 2

Дарио дал хороший прямой ответ. Если вы хотите получить более подробную информацию, там Чисто функциональные структуры данных Криса Окасаки, целая книга на эту тему. Я купил его сам, но, к сожалению, у меня нет времени экспериментировать с идеями.

Ответ 3

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

В конце концов вы назначаете новый корень для своего дерева и что тогда? Как говорит Дарио, вы передаете его как параметр функции (вместо сохранения его в глобальной переменной).

Итак,

  • Мутация поля в структуре, выделенной на куче, становится распределением новой структуры в куче.

  • Мутация глобальной переменной становится передачей параметра функции

Иногда также имеет смысл взять то, что было бы совокупностью глобальных переменных в C, и поместить их все в объект, выделенный в кучу.


P.S. Если вы действительно этого хотите, в Haskell вы можете иметь изменяемые глобальные переменные. Это, в конце концов, мир, самый важный императивный язык программирования (по словам Вадлера или Пейтона Джонса, я забываю кого). Но если вы задаете этот вопрос, вы действительно этого не хотите. Тем не менее.