Дифференциация структуры данных, построение интуиции - PullRequest
17 голосов
/ 07 мая 2011

Согласно этой статье дифференцирование работает на структурах данных.

Согласно этому ответу :

Дифференциация, производная оттип данных D (заданный как D ') - это тип D-структур с одной «дырой», то есть выделенным местоположением, не содержащим никаких данных.Это удивительно удовлетворяет тем же правилам, что и для дифференцирования в исчислении.

Правила таковы:

 1 = 0
 X′ = 1
 (F + G)′ = F' + G′
 (F • G)′ = F • G′ + F′ • G
 (F ◦ G)′ = (F′ ◦ G) • G′

Ссылочная статья слишком сложна для меня, чтобы понять ее интуицию.Что это означает на практике?Конкретный пример был бы фантастическим.

Ответы [ 2 ]

20 голосов
/ 07 мая 2011

Что такое контекст с одним отверстием для X в X? Там нет выбора: это (-), представляемый типом единицы.

Что такое контекст с одним отверстием для X в X * X? Это что-то вроде (-, x2) или (x1, -), поэтому оно может быть представлено X + X (или 2 * X, если хотите).

Что такое контекст с одним отверстием для X в X * X * X? Это что-то вроде (-, x2, x3) или (x1, -, x3) или (x1, x2, -), представимое X * X + X * X + X * X или (3 * X ^ 2, если тебе нравится).

В более общем смысле, F * G с отверстием - это либо F с отверстием и нетронутым G, либо F без изменений и G с отверстием.

Рекурсивные типы данных часто определяются как фиксированные точки многочленов.

data Tree = Leaf | Node Tree Tree

действительно говорит, что Дерево = 1 + Дерево * Дерево. Дифференцирование полинома говорит вам о контекстах для немедленных поддеревьев: в поддоне нет поддеревьев; в узле это либо отверстие слева, дерево справа или дерево слева, отверстие справа.

data Tree' = NodeLeft () Tree | NodeRight Tree ()

Это многочлен, дифференцированный и представленный как тип. Таким образом, контекст для поддерева в дереве - это список шагов этого дерева.

type TreeCtxt = [Tree']
type TreeZipper = (Tree, TreeCtxt)

Здесь, например, это функция (непроверенный код), которая ищет дерево для поддеревьев, проходящих заданное тестовое поддерево.

search :: (Tree -> Bool) -> Tree -> [TreeZipper]
search p t = go (t, []) where
  go :: TreeZipper -> [TreeZipper]
  go z = here z ++ below z
  here :: TreeZipper -> [TreeZipper]
  here z@(t, _) | p t       = [z]
                | otherwise = []
  below (Leaf,     _)  = []
  below (Node l r, cs) = go (l, NodeLeft () r : cs) ++ go (r, NodeRight l () : cs)

Роль «ниже» состоит в том, чтобы генерировать обитателей Дерева, относящихся к данному Дерева.

Разграничение типов данных - хороший способ сделать такие программы, как "поиск" generic .

11 голосов
/ 07 мая 2011

Моя интерпретация заключается в том, что производная (молния) T - это тип всех экземпляров, которые напоминают «форму» T, но ровно 1 элемент заменен на «дыру».

Например,, список равен

List t = 1     []
       + t     [a]
       + t^2   [a,b]
       + t^3   [a,b,c]
       + t^4   [a,b,c,d]
       + ...   [a,b,c,d,...]

, если мы заменим любое из этих 'a', 'b', 'c' и т. д. на отверстие (представленное как @), мы 'll получит

List' t = 0      empty list doesn't have hole
        + 1      [@]
        + 2*t    [@,b]     or [a,@]
        + 3*t^2  [@,b,c]   or [a,@,c]   or [a,b,@]
        + 4*t^3  [@,b,c,d] or [a,@,c,d] or [a,b,@,d] or [a,b,c,@]
        + ...

Другой пример, двоичное дерево - это

data Tree t = TEmpty | TNode t (Tree t) (Tree t)
-- Tree t = 1 + t (Tree t)^2

, поэтому при добавлении отверстия создается тип:

{-

Tree' t = 0                    empty tree doesn't have hole
        + (Tree X)^2           the root is a hole, followed by 2 normal trees
        + t*(Tree' t)*(Tree t) the left tree has a hole, the right is normal
        + t*(Tree t)*(Tree' t) the left tree is normal, the right has a hole

          @    or      x     or     x    
         / \          / \          / \   
        a   b       @?   b        a   @?
       /\   /\     / \   /\      /\   /\ 
      c  d e  f   @? @? e  f    c  d @? @?
-}

data Tree' t = THit (Tree t) (Tree t)
             | TLeft t (Tree' t) (Tree t)
             | TRight t (Tree t) (Tree' t)

Третий пример, иллюстрирующий правило цепочки, - это розовое дерево (дерево вариад):

data Rose t = RNode t [Rose t]
-- R t = t*List(R t)

производная говорит R' t = List(R t) + t * List'(R t) * R' t, что означает

{-

R' t = List (R t)        the root is a hole
     + t                 we have a normal root node,
       * List' (R t)       and a list that has a hole,
       * R' t              and we put a holed rose tree at the list's hole

        x
        |
       [a,b,c,...,p,@?,r,...]
                    |
                   [@?,...]

-}

data Rose' t = RHit [Rose t] | RChild t (List' (Rose t)) (Rose' t)

Обратите внимание, что data List' t = LHit [t] | LTail t (List' t).

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


Производнаясистематический способ записать, как кодировать местоположение в структуре, например, мы можем искать с помощью: (не совсем оптимизировано)

locateL :: (t -> Bool) -> [t] -> Maybe (t, List' t)
locateL _ [] = Nothing
locateL f (x:xs) | f x       = Just (x, LHit xs)
                 | otherwise = do
                                  (el, ctx) <- locateL f xs
                                  return (el, LTail x ctx)

locateR :: (t -> Bool) -> Rose t -> Maybe (t, Rose' t)
locateR f (RNode a child)
      | f a       = Just (a, RHit child)
      | otherwise = do 
                      (whichChild, listCtx) <- locateL (isJust . locateR f) child
                      (el, ctx) <- locateR f whichChild
                      return (el, RChild a listCtx ctx)

и изменять (вставлять отверстие), используя tКонтекстная информация:

updateL :: t -> List' t -> [t]
updateL x (LHit xs) = x:xs
updateL x (LTail a ctx) = a : updateL x ctx

updateR :: t -> Rose' t -> Rose t
updateR x (RHit child) = RNode x child
updateR x (RChild a listCtx ctx) = RNode a (updateL (updateR x ctx) listCtx)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...