F#: Катаморфизмы для взаимно рекурсивных структур данных

Предположим следующую взаимно рекурсивную структуру:

type Tree<'a> = 
    | Empty 
    | Node of 'a * 'a Forest
and Forest<'a> = 
    | Nil 
    | Cons of 'a Tree * 'a Forest

Цель: создать общие катаморфизмы для этой структуры: foldl, foldr, foldk.

Я породил наивный катаморфизм следующим образом:

let rec foldTree fEmpty fNode fNil fCons = 
    function 
    | Empty -> fEmpty
    | Node (a, f) -> fNode a (foldForest fEmpty fNode fNil fCons f)
and foldForest fEmpty fNode fNil fCons =
    function
    | Nil -> fNil
    | Cons (t, f') -> fCons (foldTree fEmpty fNode fNil fCons t) (foldForest fEmpty fNode fNil fCons f')

Как мне "механически" генерировать хвост-рекурсивный фолд (используя аккумуляторы) и хвост-рекурсивный фолд (используя продолжения)?

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

PS: Можно избавиться от описанной выше взаимной рекурсии с помощью встроенной функции, но давайте оставим ее, поскольку она представляет собой упрощенную версию взаимной рекурсии в анализаторе tpetricek Markdown.

1 ответ

Я абсолютно не уверен, что это то, что вы ищете, но это, кажется, дает то, что вы хотите (вроде).
Ключевым моментом является обработка только того, что находится "внутри" типа, а то, что "снаружи", обрабатывается чем-то другим (некоторая абстракция).

//val foldTree : 'a -> ('b -> 'c -> 'a) -> ('b Forest -> 'c) -> 'b Tree -> 'a
let foldTree fEmpty fNode fForest = function
  Empty       -> fEmpty
| Node (a, f) -> fNode a (fForest f)

// val foldForest : 'a -> ('b -> 'a -> 'a) -> ('c Tree -> 'b) -> 'c Forest -> 'a
let rec foldForest fNil fCons fTree =
  let recurse = foldForest fNil fCons fTree
  function
    Nil         -> fNil
  | Cons (t, f) -> fCons (fTree t) (recurse f)

let foldForestAcc fNil fCons fTree =
  let rec aux acc = function
    Nil         -> acc
  | Cons (t, f) -> aux (fCons (fTree t) acc) f
  aux fNil

let foldForestCont fNil fCons fTree =
  let rec aux cont = function
    Nil         -> cont fNil
  | Cons (t, f) -> aux (fCons (fTree t) >> cont) f
  aux id

Вот также альтернатива, если она больше подходит для того, что вы ищете:

let fold fEmpty fNode fNil fCons =
  let rec auxT = function
    Empty       -> fEmpty
  | Node (a, f) -> fNode a (auxF f)
  and auxF = function
    Nil         -> fNil
  | Cons (t, f) -> fCons (auxT t) (auxF f)
  auxT

let foldAcc fEmpty fNode fNil fCons =
  let rec auxT acc = function
    Empty       -> acc
  | Node (a, f) -> fNode a (auxF fNil f)
  and auxF acc = function
    Nil         -> acc
  | Cons (t, f) -> auxF (fCons (auxT fEmpty t) acc) f
  auxT fEmpty

let foldCont fEmpty fNode fNil fCons =
  let rec auxT cont = function
    Empty -> cont fEmpty
  | Node (a, f) -> cont (fNode a (auxF id f))
  and auxF cont = function
    Nil -> cont fNil
  | Cons (t, f) -> auxF (cont >> fCons (auxT id t)) f
  auxT id
Другие вопросы по тегам