Обход типа данных с рекурсивным списком двоичных деревьев в функции

Я создал рекурсивное бинарное дерево, в котором может быть неограниченное количество детей:

data Tree a = Leaf a
            | Node [Tree a]

Моя проблема заключается в синтаксисе при создании функции для обхода дерева.

Скажем, я хочу сделать что-то простое, например дать аргумент 1, а затем узнать Bool, встречается ли 1 в дереве. У меня проблема с определением функции, когда Node является [Tree a] вместо Node [Leaf root Leaf]. Я не привык к функции, вызывающей список в переменной.

Вот как это будет работать с более простым рекурсивным типом данных:

occs :: Eq a => a -> Tree a -> Bool 
occs x (Leaf y) = x == y
occs x (Node left y right) = x == y || occs x left || occs x right

Однако (Node left y right) больше не является правильным, потому что Node теперь является перечисленным деревом [Tree a]. Как вы можете написать перечисленное дерево [Tree a] в качестве переменной для управления?

Ожидаемые результаты будут True или False, если он появится, но проблема в основном заключается в синтаксисе идентификации перечисленных [Tree a] в функции. Я пробовал несколько способов написать его, и он всегда возвращает ошибку.

Мы не сможем помочь вам понять, что пошло не так с вашей попыткой, если вы не покажете нам свою попытку. Поэтому включите код, который, по вашему мнению, должен работать, но не работает, вместе с полученным сообщением об ошибке.

Daniel Wagner 10.04.2019 15:55

В Leaf y вы сопоставление с образцом конструктор Leaf a. Допустимые совпадения для вашего конструктора Node [Tree a]: Node _, Node [], Node [x] (или Node (x:[])), Node [x,y] (или Node (x:y:[])) и так далее.

Micha Wiedenmann 10.04.2019 16:13

Какое определение для example4?

Daniel Wagner 10.04.2019 19:29
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
3
124
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Вы просто хотите рекурсивно проверить, содержит ли какая-либо из ветвей (именно так я называю деревья в списке) x. К счастью, в Prelude есть функция Любые, которая упрощает эту задачу:

occs :: Eq a => a -> Tree a -> Bool 
occs x (Leaf y) = x == y
occs x (Node branches) = any (occs x) branches

(any тоже очень легко реализовать самому при желании)

Таким образом, вы можете назвать его как хотите, и он просто представляет собой список. Но последний вопрос: как мне создать пример для тестирования. example :: Tree a example = Node [(Leaf 1) (Leaf 7) (Leaf 8)]

user11340751 10.04.2019 16:08

это выглядит нормально, хотя тип Tree a Int (вместо Int подойдет любой числовой тип, но на самом деле это не имеет значения).

Robin Zigmond 10.04.2019 16:13

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

user11340751 10.04.2019 16:13

Да, все, что я пытался сказать, это не какая-то "волшебная" функция, предоставляемая нам стандартной библиотекой - как и большинство библиотечных функций, она существует для удобства, но может быть легко реализована в нескольких строках Haskell, если вам нужно . Поскольку он предоставляется нам, вам никогда не нужно писать его самостоятельно, кроме как в качестве упражнения.

Robin Zigmond 10.04.2019 16:21

Не могли бы вы показать мне? Было бы очень полезно понять функцию. Большое спасибо, что помогли мне, кстати! Ответ был очевиден, как только я его увидел, но написание примеров для его проверки отняло у меня некоторое время.

user11340751 10.04.2019 16:24

в комментарии нет места, чтобы сделать это, конечно, не для того, чтобы правильно изложить это, но я настоятельно рекомендую вам попробовать это самостоятельно, но вот несколько важных советов. Это очень простая рекурсивная функция: базовый случай — any p [] = False, рекурсивный случай — any p (x:xs), вам нужно проверить, является ли p x истинным или ложным, и дать соответствующий результат (один включает рекурсивный вызов any p xs, другой может дать ответ сразу ).

Robin Zigmond 10.04.2019 16:30

Я создал соответствующую ей функцию, но она не работает в реальной функции, где использовалось any. helper p [] = False helper p (x:xs) | p == x = True | otherwise = helper p xs

user11340751 10.04.2019 18:39

Нет, не будет. Подумайте об этом логически. Вы хотите сказать, удовлетворяет ли какой-либо элемент в списке p. Итак, вы тестируете p на первом элементе: если он проходит, нужно ли вам проверять остальную часть списка? Если это не удается, вы хотите дать немедленный ответ «нет»? Это то, что делает ваша функция выше.

Robin Zigmond 10.04.2019 18:41

Итак, я написал этот новый, и он компилируется, но когда я проверяю его на примере, он выдает ошибку. Код: helper p [] = False helper p (x : xs) | p x = True | otherwise = helper p xs

user11340751 10.04.2019 18:53
* No instance for (Eq (Tree2 Int)) arising from a use of occs * В выражении: occs example5` In an equation for it: it = occs example5`
user11340751 10.04.2019 19:21

Выкладываю полный код. Он не будет тестировать пример, но он компилируется. Я не уверен, что означает сообщение об ошибке выше.

user11340751 10.04.2019 19:26

@Drake Судя по вашей ошибке, у вас есть example5, которое, как я полагаю, является деревом, и вы звоните occs example5 для запуска теста. Но occs принимает два аргумента, а не один. Это должно быть, например. occs 7 example5.

DarthFennec 10.04.2019 20:00

ДУХХХХХ! Я такой идиот, ты совершенно прав. Я был настолько сосредоточен на коде, что забыл, что он делает. Всем большое спасибо, вы все были восхитительны

user11340751 10.04.2019 20:01

Поскольку Робин Зигмонд дал ответ на ваш вопрос: «Как вы можете написать перечисленное дерево [Tree a] в качестве переменной для манипулирования?», Я попытаюсь ответить по-другому.

I created a recursive binary tree where there can be unlimited children:

data Tree a = [...]

Тогда он не бинарный, а н-арный.

Этот точный тип уже существует в пакете containers в модуле Data.Tree.

Data.Tree для него определено несколько экземпляров классов типов, и один из них — Foldable, а функция, которую вы пишете, уже находится в Data.Foldable под именем elem и имеет тип:

elem :: (Eq a, Foldable t) => a -> t a -> Bool

Поэтому, если вы выбрали тип данных н-арное дерево, Data.Tree и elem будут правильным выбором.


Если, с другой стороны, вам нужно фактическое двоичное дерево с двумя дочерними узлами, определение этого и создание экземпляра Foldable даст вам elem таким же образом:

{-# LANGUAGE DeriveFoldable #-}

data BinaryTree a
  = Leaf
  | Node a (BinaryTree a) (BinaryTree a)
  deriving (Foldable)

-- now `elem` comes for free

Редактировать: По предложению amalloy вместо этого был получен экземпляр Foldable.

Но поскольку elem ограничен только Eq a, а не Ord a, вы не можете получить поиск О (журнал п), как вы могли бы сделать для двоичного дерева поиск, поэтому вам придется называть его как-то иначе, если вы хотите:

occs :: Ord a => a -> BinaryTree a -> Bool
occs _ Leaf = False
occs x (Node y left right) =
  case x `compare` y of
    LT -> occs x left
    EQ -> True
    GT -> occs x right

Написание экземпляров Foldable вручную быстро утомляет. Вместо этого создайте их с помощью DeriveFoldable.

amalloy 10.04.2019 21:51

Другие вопросы по теме