сопоставить функцию с деревом в haskell - PullRequest
1 голос
/ 23 декабря 2011

Мы можем определить двоичное дерево следующим образом:

data Tree a = Node a (Tree a) (Tree a)
            | Empty
            deriving (Show)

Теперь у меня есть функция, скажем (+), как я могу сопоставить ее с двоичным деревом?Как я могу сопоставить произвольную функцию двоичному дереву?
Так что (+) a b будет

Node (+) a (Node (+) Empty Empty) (Node a Empty Empty))  

Это домашняя работа, которая просит нас сопоставить функцию с деревом, и вышеизложенное - как японять это.
Объявление функции может быть:

 functionToTree :: (t1 -> t2) -> Tree a

Я задержан на определении типа функции, чье число аргументов является переменной .

РЕДАКТИРОВАТЬ : Извините, как сказал nponeccop, я неправильно понял свою задачу и знал, как написать functionToTree :: (a -> b) -> Tree a -> Tree b.Несмотря на это, мне все еще интересно узнать мой оригинальный вопрос.Чтобы пояснить немного, вот что я думал:

                      (+) a 
                        /  \
                      (+)   a

Что касается функции f, принимающей три параметра a b c:

                     f a b 
                      /   \
                    f a    b
                    /  \
                   f    a

Хорошо, это больше не моя домашняя работа.Мне просто интересно, можно ли написать такую ​​функцию.Мы можем определить список следующим образом:

data Li a = Cons a (Li a)
          | Empty
          deriving (Show, Eq, Order)

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

БОЛЬШЕ: Я уточнил свой вопрос.Я думаю, что мое дерево - это еще один способ проиллюстрировать частичную функцию и карри.

Ответы [ 2 ]

1 голос
/ 23 декабря 2011

Вы плохо поняли задачу. При сопоставлении функции с деревом функция применяется к каждому элементу данных, содержащемуся в дереве.

Я предлагаю вам начать с рисования дерева, содержащего числа.

             1
           /   \
          2     3
           \   / \
            4 6   5  

Можете ли вы закодировать это дерево, используя тип данных Tree a, то есть написать дерево как выражение с помощью конструкторов Node и Empty?

Вот несколько подсказок:

  • Верхний узел содержит 1 и два непустых поддерева.

  • Левое поддерево содержит 2, одно пустое поддерево и одно непустое поддерево.

  • Правое поддерево содержит 3 и два непустых поддерева.

  • Все узлы третьего уровня - это деревья с пустыми поддеревьями.

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

Вы нарисовали свое дерево неправильно. Правильное дерево:

              f 1
            /     \
         f 2       f 3
            \     /   \
           f 4  f 6   f 5

Таким образом, вы можете отображать функции только с одним аргументом, но не с двумя, тремя или более.

Идея состоит в том, что вы можете (например) добавить два к каждому элементу дерева. Так что если вы передадите

             1
           /   \
          2     3           and (\x -> x + 2) or equivalently (+2)
           \   / \
            4 6   5  

к вашей функции, например tree2 = functionToTree (+2) tree1, вы получите исправленное дерево:

             3
           /   \
          4     5
           \   / \
            6 8   7

Таким образом, каждый элемент дерева заменяется новым элементом.

0 голосов
/ 23 декабря 2011

Учитывая функциональную структуру данных (в данном случае, дерево), обычно есть две основные вещи, которые вы можете сделать с ней:

  1. map
  2. fold

При сопоставлении вы берете функцию f :: a -> b и структуру origTree :: Tree a и применяете функцию к элементам структуры, в результате чего получается newTree :: Tree b.(Обратите внимание, что канонический способ сделать структуру сопоставимой - это сделать ее Functor и определить fmap)

Складывание - это то, где вы каким-то образом соединяете все элементы структуры вкакое-то новое значение.Когда вы сказали, что у вас есть функция Tree и (+), я сразу подумал о складывании: суммировании всех элементов в дереве.(Обратите внимание, что канонический способ сделать структуру складной - это сделать ее экземпляром Foldable (сюрприз!) И определить foldMap или foldr)

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


Теперь, касаясь вашего собственного вопроса, превращаем функцию в дерево.Немного неясно, что именно вы подразумеваете под «a», «1031» и «1032» в своем дереве, но давайте немного поиграем с идеей.Ради простоты я не собираюсь делать полностью общую функцию.Кроме того, поскольку ваши функции "деревья" довольно однобокие, я называю их FunHistory вместо Tree.Это будет представлять историю приложений функций.

data FunHistory a b = Result b (FunHistory a b)
                    | Application (a -> FunHistory a b) a (FunHistory a b)
                    | Base (a -> FunHistory a b)

Теперь этот тип немного странный.Result содержит результат типа b, а также ссылку на предыдущую историю приложений функций.Base содержит функцию с историей приложений функций и возможностью создания будущей истории, если задано значение типа a.Application, следовательно, является промежуточной записью, которая предоставляет возможность создания будущей истории, а также отмечает прошлую историю и какое значение было применено к этой прошлой истории.

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

mkHist :: (a -> b) -> FunHistory a b
mkHist f = let h = Base (\x -> Result (f x) h) in h

Используя функцию с одним аргументом, мы можем создать из нее историю ... магией.Этот специфический вкус магии называется «лень» и «рекурсивное разрешение».

Давайте продолжим и создадим функцию, которая будет принимать FunHistory и входное значение, и перемещать историю дальше.К сожалению, это не полная функция;он не определен для Result типа FunHistory.

-- The caller should make sure it isn't a `Result` type before using this function
app :: a -> FunHistory a b -> FunHistory a b
app x (Result _ _)        = undefined
app x (Application f _ _) = f x
app x (Base f)            = f x

Это прекрасно и просто для функций с одним аргументом, но промежуточный конструктор Application никогда не требуется для таких простых случаев.Давайте попробуем создать смарт-конструктор для функции с двумя аргументами:

mkHist2 :: (a -> a -> b) -> FunHistory a b
mkHist2 f = let h = Base (\x -> mkHist' f x h)
            in h

mkHist' f x h = let h' = Application (\y -> Result (f x y) h') x h
                in h'

Давайте попробуем это сейчас для функции с тремя аргументами:

mkHist3 :: (a -> a -> a -> b) -> FunHistory a b
mkHist3 f = let h = Base (\x -> mkHist2' f x h)
            in h

mkHist2' f x h = let h' = Application (\y -> mkHist' (f x) y h') x h
                 in h'

Теперь функция с 4 аргументами:

mkHist4 :: (a -> a -> a -> b) -> FunHistory a b
mkHist4 f = let h = Base (\x -> mkHist3' f x h)
            in h

mkHist3' f x h = let h' = Application (\y -> mkHist2' (f x) y h') x h
                 in h'

Хорошо, посмотрите на это;эти функции выглядят почти так же, как mkHist3 и mkHist2' соответственно!Следующим шагом здесь будет обобщение этих функций в класс типов, чтобы он масштабировался до функций с произвольным числом входов.Подвох в том, что все входы должны иметь одинаковый тип.

[предупреждение: этот код не проверен, но я несколько уверен, что он в основном корректен ... ish]

instance (Show a, Show b) => Show (FunHistory a b) where
  show (Base _) = "base"
  show (Application _ x h) = "app " ++ show x ++ ", " ++ show h
  show (Result r h) = "result: " ++ r ++ ", " ++ show h
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...