В компьютерном программировании анаморфизм — это функция, которая генерирует последовательность путем повторного применения функции к ее предыдущему результату. Вы начинаете с некоторого значения A и применяете к нему функцию f, чтобы получить B. Затем вы применяете f к B, чтобы получить C, и так далее, пока не будет достигнуто некоторое конечное условие. Анаморфизм — это функция, которая генерирует список A, B, C и т. д. Вы можете думать об анаморфизме как о развертывании начального значения в последовательность.
Вышеприведенное неспециалисту описание может быть сформулировано более формально в теории категорий : анаморфизм коиндуктивного типа обозначает присвоение коалгебры ее уникальному морфизму к конечной коалгебре эндофунктора . Эти объекты используются в функциональном программировании как разворачивания .
Категориальной двойственностью (иначе говоря, противоположностью) анаморфизма является катаморфизм .
В функциональном программировании анаморфизм — это обобщение концепции развёрток на коиндуктивных списках . Формально анаморфизмы — это общие функции , которые могут корекурсивно конструировать результат определённого типа и которые параметризуются функциями, определяющими следующий отдельный шаг построения.
Рассматриваемый тип данных определяется как наибольшая неподвижная точка ν X . FX функтора F . По универсальному свойству конечных коалгебр существует единственный морфизм коалгебр A → ν X . FX для любой другой F -коалгебры a : A → FA . Таким образом, можно определить функции из типа A _в_ коиндуктивный тип данных, указав структуру коалгебры a на A .
Например, тип потенциально бесконечных списков (с элементами фиксированного типа value ) задается как фиксированная точка [value] = ν X . value × X + 1 , т.е. список состоит либо из value и дополнительного списка, либо он пуст. (Псевдо-) Haskell -определение может выглядеть так:
данные [ значение ] = ( значение : [ значение ]) | []
Это неподвижная точка функтора F value
, где:
данные Может быть a = Просто a | Ничего данные F значение x = Может быть ( значение , x )
Можно легко проверить, что тип действительно [value]
изоморфен F value [value]
, и, таким образом, [value]
является неподвижной точкой. (Также следует отметить, что в Haskell наименьшая и наибольшая неподвижные точки функторов совпадают, поэтому индуктивные списки совпадают с коиндуктивными, потенциально бесконечными списками.)
Анаморфизм для списков (тогда обычно известный как unfold ) будет строить (потенциально бесконечный) список из значения состояния. Обычно unfold принимает значение состояния и функцию , которая выдает либо пару значения и нового состояния, либо синглтон, чтобы отметить конец списка. Затем анаморфизм начнется с первого семени, вычислит, продолжается ли список или заканчивается, и в случае непустого списка добавит вычисленное значение к рекурсивному вызову анаморфизма.x
f
Определение развёртки, или анаморфизма для списков, на языке Haskell ana
, выглядит следующим образом:
ana :: ( state -> Maybe ( value , state )) -> state -> [ value ] ana f stateOld = case f stateOld of Nothing -> [] Just ( value , stateNew ) -> value : ana f stateNew
Теперь мы можем реализовать довольно общие функции с помощью ana , например, обратный отсчет:
f :: Int -> Maybe ( Int , Int ) f current = let oneSmaller = current - 1 in if oneSmaller < 0 then Nothing else Just ( oneSmaller , oneSmaller )
Эта функция будет уменьшать целое число и выводить его одновременно, пока оно не станет отрицательным, в этот момент она отметит конец списка. Соответственно, ana f 3
вычислит список [2,1,0]
.
Анаморфизм может быть определен для любого рекурсивного типа в соответствии с общим шаблоном, обобщающим вторую версию ana для списков.
Например, развёртка для древовидной структуры данных
данные Дерево a = Лист a | Ветвь ( Дерево a ) a ( Дерево a )
выглядит следующим образом
ana :: ( b -> Либо a ( b , a , b )) -> b -> Дерево a ana unspool x = случай unspool x из Left a -> Лист a Right ( l , x , r ) -> Ветвь ( ana unspool l ) x ( ana unspool r )
Чтобы лучше увидеть связь между рекурсивным типом и его анаморфизмом, отметим, что Tree
и List
можно определить следующим образом:
newtype Список a = Список { unCons :: Может быть ( a , Список a )} newtype Дерево a = Дерево { unNode :: Либо a ( Дерево a , a , Дерево a ))}
Аналогия с ana
появляется при переименовании b
его типа:
newtype Список a = Список { unCons :: Может быть ( a , Список a )} anaList :: ( list_a -> Может быть ( a , список_a )) -> ( list_a -> Список a ) newtype Дерево a = Дерево { unNode :: Либо a ( Дерево a , a , Дерево a ))} anaTree :: ( tree_a -> Либо a ( tree_a , a , tree_a )) -> ( tree_a -> Дерево a )
При таких определениях аргумент конструктора типа имеет тот же тип, что и возвращаемый тип первого аргумента ana
, а рекурсивные упоминания типа заменяются на b
.
Одной из первых публикаций, в которой было введено понятие анаморфизма в контексте программирования, была статья « Функциональное программирование с бананами, линзами, конвертами и колючей проволокой » [1] Эрика Мейера и др. , написанная в контексте языка программирования Squiggol .
Функции типа zipи iterate
являются примерами анаморфизмов. zip
принимает пару списков, скажем ['a','b','c'] и [1,2,3] и возвращает список пар [('a',1),('b',2),('c',3)]. Iterate
принимает элемент x и функцию f, преобразующую такие элементы в такие элементы, и возвращает бесконечный список, который получается в результате многократного применения f, т. е. список [x, (fx), (f (fx)), (f (f (fx))), ...].
zip ( a : as ) ( b : bs ) = if ( as == [] ) || ( bs == [] ) -- || означает «или», то [( a , b )] else ( a , b ) : ( zip as bs ) iterate f x = x : ( iterate f ( f x ))
Чтобы доказать это, мы можем реализовать оба варианта, используя нашу универсальную развертку ana
, используя простую рекурсивную процедуру:
zip2 = ana unsp fin , где fin ( as , bs ) = ( as == [] ) || ( bs == [] ) unsp (( a : as ), ( b : bs )) = (( a , b ),( as , bs )) iterate2 f = ana ( \ a -> ( a , f a )) ( \ x -> Ложь )
В таком языке, как Haskell, даже абстрактные функции fold
и являются просто определяемыми терминами, как мы видели из определений, данных выше unfold
.ana
В теории категорий анаморфизмы являются категориальным дуалом катаморфизмов ( а катаморфизмы являются категориальным дуалом анаморфизмов).
Это означает следующее. Предположим, что ( A , fin ) является конечной F -коалгеброй для некоторого эндофунктора F некоторой категории в себя. Таким образом, fin является морфизмом из A в FA , и поскольку он предполагается конечным, мы знаем, что всякий раз, когда ( X , f ) является другой F -коалгеброй (морфизмом f из X в FX ), будет существовать единственный гомоморфизм h из ( X , f ) в ( A , fin ), то есть морфизм h из X в A такой, что fin . h = Fh . f . Тогда для каждого такого f мы обозначаем через ana f этот однозначно заданный морфизм h .
Другими словами, у нас есть следующее определяющее соотношение, заданное некоторыми фиксированными F , A и fin , как указано выше:
В литературе встречается обозначение ana f . Используемые скобки известны как линзовые скобки , после чего анаморфизмы иногда называют линзами .
{{cite journal}}
: Цитировать журнал требует |journal=
( помощь )