Изменения

Перейти к: навигация, поиск

Функциональное программирование

15 666 байт добавлено, 19:05, 4 сентября 2022
м
rollbackEdits.php mass rollback
=== Решение ===
В нормальной форме нет редукций. Если нормальная форма существует, то её можно достичь при помощи редукций [[#Нормальный порядок редукции|нормальным порядком]], а [[#Аппликативный порядок редукции|аппликативным ]] можно и не достичь.
# Уже в нормальное форме, как ни странно
[http://pv.bstu.ru/flp/fpLectures.pdf Здесь] ещё про стратегии редуцирования, но немного другим языком (может быть, кому-то более понятным).
'''Аппликативный порядок редуцирования''' {{---}} первым делом редуцируем самый правый левый самый глубокий терм. То есть сначала упрощаем "аргументы" аппликации.
Те же примеры (во всех случаях одна редукция будет произведена):
((λ x . y) (λ z . t)) ((λ a b c . a b c ((λ s . t) y) (λ t . x) u) (λ x . x)) ((λ x . x x) z) <tex> \Rightarrow </tex>
((λ x . y) (λ z . t)) ((λ a b c . a b c ((λ s . t) y) (λ t . x) u) (λ x . x)) (z z)
 
== Ленивый порядок редукции ==
 
'''Ленивый порядок редуцирования''' {{---}} это когда мы якобы заворачиваем терм в коробку, и если делаем редукцию в одном из термов коробки, то она делается во всех. При этом сам порядок редуцирования нормальный.
 
То есть пример:
 
(λ f . f f) ((λ x . x) z)
 
Сначала делаем обычную редукцию нормальным порядком и получаем:
 
((λ x . x) z) ((λ x . x) z)
 
А потом после редукции нормальным порядком надо сделать изменения сразу в двух термах, потому что они якобы в коробках, и получим суммарно за 2 редукции:
 
z z
==Выписать систему уравнений типизации==
== E2. let-биндинги, но с возможной взаимной рекурсией ==
foo = foo ((\ a . bar)foo) bar = (\ a . y) y (\ b . y) main = (\ a . foo (a z) (y y)) y === Решение ===<font color=magenta> '''Осторожно! Магия''' </font> Расписывать формальный алгоритм довольно нудно и неприятно, поэтому здесь объяснение того, что происходит, а на примере должно быть понятно. Сначала по каждому терму из условия надо составить терм с таким же именем, только штрихованный. В нём мы будем использовать первые буквы остальных термов. Фиксируем порядок аргументов, например для foo' это будет f b m. Тогда у всех остальных термов будет циклический порядок. То есть для bar' будет b m f, а для main' {{---}} m f b. Теперь пишем foo'. Сначала используем fix. Потом абстракцию, аргументами которой является нужный набор из циклических перестановок (см. соответствие выше), а телом абстракции является тело foo с изменениями. Если встречается имя терма из задания, то надо его заменить на нужный циклический порядок. И если имена первых букв по каким-то причинам не подходят (коллизятся со связанными переменными), то надо более удачные имена этим переменным придумать. Итого, после преобразований:  foo' = fix (\ f b m . f b m ((\ a . b m f) (f b m))) bar' = fix (\ b m f . (\ a . y) y (\ b . y)) main' = fix (\ m f b . (\ a . f b m (a z) (y y)) y) Результирующий терм выглядит как вызов штрихованной функции в нужной циклической перестановке main = main' foo' bar' Но так как в этом задании дополнительные термы использовать нельзя, то main', foo', bar' надо проинлайнить в main.
== N2. Раскрыть, как в E1 и нормализовать ==
Интересное наблюдение: переменная p в case является как раз нужным конструктором, в котором уже подставлены все аргументы этого конструктора.
== A2. Закодировать типы по Чёрчу через <tex> \mu </tex> - комбинатор ==  '''data''' Return a b = List (Return b a) (Return b a) b | Roll (Return a a) (Return a a) (Mice a) '''data''' Mice a = Bucket (Mice a) (Return a a) | Haystack (с взаимной рекурсиейMice a) a === Решение === <font color=magenta> '''Магия как в E2!''' </font> Опять делаем соотвествие между TypeName и TypeName'. Чтобы написать TypeName', необходимо преобразовать объявление дататипов по следующим правилам:* сначала идёт mu (это как fix, только для типов),* потом какая-нибудь уникальная буква для типа (например x для Return и y для Mice),* после точки абстракция, которая сначала принимает в качестве аргумента другую уникальную букву, а затем аргументами параметры типа,* T1 | T2 заменяется на T1 + T2,* T1 T2 заменяется на T1 <tex> \times </tex> T2,* параметры типа оставляем как есть,* если в конструкторе идёт наш тип, то пишем нашу уникальную букву, а затем уникальную букву другого типа, а если другой типа {{---}} наоборот; после чего параметры конструктора,* если тип не наш и не буковка параметр датайпа, и не принимает параметров (например Nothing), то пишем 1 вместо неё.
Return' = mu x . \ y . \ a b . (x y b a)<tex> \times </tex> (x y b a) <tex> \times </tex> b + (x y a a) <tex> \times </tex> (x y a a) <tex> \times </tex> (y x a) Mice' = mu y . \ x . \ a . (y x a) <tex> \times </tex> (x y a a) + (y x a) <tex> \times </tex> a После этого пишем ответ Return = Return' Mice' Mice = Mice' Return'
== H1. Написать Haskell-код какой-нибудь структуру данных ==
* [[АВЛ-дерево]]: [http://pastie.org/private/qbiu60aetjm9zrpqzrow ссылка на pastie]*: почему я не знал Haskell, когда это дерево было в лабе по дискретке на первом курсе? ;( просто списывается с конспекта один в один...* [[Квадродеревья | Квадродерево]]: [http://pastiepastebin.org/privatecom/sf1vdmrpe7ifvqgdongwq jV4DeRvv ссылка на pastiepastebin]
*: не совсем то, что требует Ян, но я пока не распарсил то, что он требует; возможно, более правильная версия появится позже
#* [https://github.com/itanf/ITMO-Training-FunctionalProgramming/blob/master/ITMOPrelude/Primitive.hs Primitive.hs]
#* [https://github.com/itanf/ITMO-Training-FunctionalProgramming/blob/master/ITMOPrelude/List.hs List.hs]
 ==Primitive=====Nat=Натуральные числа== data Nat = Zero | Succ Nat deriving (+.) (-.) (Show,Read*.) <font color=green  divides :: Nat ->Nat -> Bool ===Rat=== data Rat (%+) (%- Определение натуральных чисел<) (%*) (%/font)  euler :: ? ==List== ===Угадайка===Дают тип, надо написать название функции из '''List.hs''' и реализовать её. ===Комбинаторика=== '''Тут можно использовать только набор заранее определённых функций листа( среди которых нет даже ''++'' )'''  subsequences :: [a] ->[ [ a ] ]  natZero permutations :: [a] -> [ [ a ] ] ==Algebra= Zero <font color=green class Monoid a where mempty :: a mappend :: a ->a -> a class Monoid a => Group a where ginv :: a - 0</font>a natOne = Succ Zero <font colormconcat :: (Monoid a) =green>List a -- 1</font>a mconcat = foldr mappend mempty
natCmp :: Nat -> Nat -> Tri <font color=green>-- Сравнивает два натуральных числа</font>instance Monoid Unit where natCmp Zero Zero mempty = EQUnit natCmp Zero (Succ mappend _) = LT natCmp (Succ _) Zero = GT natCmp (Succ n) (Succ m) = natCmp n mUnit
natEq :: Nat -> Nat -> Bool <font color=green>-- n совпадает с m</font> natEq Zero Zero = True natEq Zero (Succ _) = Falseinstance Group Unit where natEq (Succ ginv _) Zero = False natEq (Succ n) (Succ m) = natEq n mUnit
natLt :: Nat -> Nat -> Bool <font colorinstance (Monoid a, Monoid b) =green>-- n меньше m</font>Monoid (Pair a b) where natLt Zero Zero mempty = FalsePair mempty mempty natLt Zero (Succ m) mappend a b = True natLt (Succ n) Zero Pair {fst = Falsefst a `mappend` fst b, natLt (Succ n) (Succ m) snd = natLt n msnd b `mappend` snd b}
infixl 6 +. <font colorinstance (Monoid a) =green>-- Сложение для натуральных чисел</font> Monoid (+.Maybe a) :: Nat -> Nat -> Natwhere Zero +. m mempty = mJust mempty mappend (Succ nJust a) +. m = Succ (n +. mJust b)= Just $ mappend a b mappend _ _ = Nothing
infixl 6 -. <font colornewtype First a =green>-- Вычитание для натуральных чисел</font> (-.) First { getFirst :: Nat -> Nat -> Nat Zero -. _ = Zero n -. Zero = n (Succ n) -. (Succ m) = n -. mMaybe a}
infixl 7 *. <font color=green>-- Умножение для натуральных чисел</font> instance Monoid (*.First a) :: Nat -> Nat -> Natwhere Zero *. m mempty = ZeroFirst Nothing mappend (Succ nFirst Nothing) *. m x = x mappend x _ = m +. (n *. m)x
natDivMod newtype Last a = Last { getLast :: Nat -> Nat -> Pair Nat Nat <font color=green>-- Целое и остаток от деления n на m</font> natDivMod n m = if (n natLt m) then Pair Zero n else Pair (Succ div) mod where Pair div mod = ((n -. m) natDivMod m)Maybe a}
natDiv n instance Monoid (Last a) where mempty = Last Nothing mappend x (Last Nothing) = x mappend _ x = x newtype Any = fst . natDivMod n <font colorAny { getAny :: Bool } instance Monoid Any where mempty = Any False mappend (Any a) (Any b) = Any $ a || b newtype All = All { getAll :: Bool } instance Monoid All where mempty = All True mappend (All a) (All b) =green>All $ a && b -- Целое</font>Лексикографическое сравнение instance Monoid Tri where mempty = EQ mappend LT _ = LT mappend EQ a = a mappend GT _ = GT newtype Sum a = Sum { getSum :: a } instance Monoid (Sum Nat) where mempty = Sum natZero mappend (Sum a) (Sum b) = Sum $ a +. b newtype Product a = Product { getProduct :: a } instance Monoid (Product Nat) where mempty = Product natOne mappend (Product a) (Product b) = Product $ a *. b natMod n instance Monoid (Sum Int) where mempty = Sum intZero mappend (Sum a) (Sum b) = snd Sum $ a .+. natDivMod n <font colorb instance Group (Sum Int) where ginv =green>-- Остаток</font>Sum . intNeg . getSum instance Monoid (Product Int) where mempty = Product intOne mappend (Product a) (Product b) = Product $ a .*. b instance Monoid (Sum Rat) where mempty = Sum ratZero mappend (Sum a) (Sum b) = Sum $ a %+ b instance Group (Sum Rat) where ginv = Sum . ratNeg . getSum instance Monoid (Product Rat) where mempty = Product ratOne mappend (Product a) (Product b) = Product $ a %* b instance Group (Product Rat) where ginv = Product . ratInv . getProduct instance Monoid (List a) where mempty = Nil mappend = (++)
==Целые числаCategories==  data Int = Plus Nat | Minus Nat deriving class Category cat where id :: cat a a (Show,Read.):: cat b c -> cat a b -> cat a c
intZero = Plus Zeroclass Functor f where intOne = Plus fmap :: (Succ Zero) intNegOne = Minus (Succ Zeroa -> b)-> f a -> f b
intNeg class Monad m where (>>=) :: Int m a -> Int intNeg (Plus xa -> m b) = Minus x-> m b intNeg (Minus x) = Plus x return :: a -> m a
intCmp class (Functor f) => Applicative f where pure :: Int a -> Int -> Trif a intCmp (Plus Zero<*>) :: f (Minus Zeroa -> b) = EQ intCmp (Minus Zero) (Plus Zero) = EQ intCmp (Plus Zero) (Minus (Succ x)) = GT intCmp (Minus Zero) (Plus (Succ x)) = LT intCmp (Plus (Succ x)) (Minus Zero) = GT intCmp (Minus (Succ x)) (Plus Zero) = LT intCmp (Plus x) (Plus y) = natCmp x y intCmp (Minus x) (Minus y) = natCmp y x-> f a -> f b
intEq class Functor m => MonadJoin m where returnJoin :: Int -> Int a -> Boolm a intEq join :: m (Plus Zerom a) (Minus Zero) = True intEq (Minus Zero) (Plus Zero) = True intEq (Plus Zero) (Minus (Succ x)) = False intEq (Minus Zero) (Plus (Succ x)) = False intEq (Plus (Succ x)) (Minus Zero) = False intEq (Minus (Succ x)) (Plus Zero) = False intEq (Plus x) (Plus y) = natEq x y intEq (Minus x) (Minus y) = natEq x y-> m a
intLt :: Int -> Int -> Bool intLt (Plus Zero) (Minus Zero) = False intLt (Minus Zero) (Plus Zero) = False intLt (Plus Zero) (Minus (Succ x)) = False intLt (Minus Zero) (Plus (Succ x)) = True intLt (Plus (Succ x)) (Minus Zero) = False intLt (Minus (Succ x)) (Plus Zero) = True intLt (Plus x) (Plus y) data Identity a = natLt x yIdentity a intLt (Minus x) (Minus y) runIdentity a = natLt y xa
infixl 6 .+., .-.instance Monad Identity where (.+.) :: Int -> Int -> Int (Plus m) .+. (Plus n) = Plus (m +. n) (Minus m) .+. (Minus n) = Minus (m +. n) (Plus (Succ m)) .+. (Minus (Succ n)) = (Plus m) .+. (Minus n) (Minus (Succ m)) .+. (Plus (Succ n)) = (Plus m) .+. (Minus n) return x .+. (Plus Zero) = Identity x x .+. (Minus Zero) = Identity x (Plus Zero) .+. y >>= y (Minus Zero) .+. y f = yf x
(.-.) :: Int -> Int -> Int n .-. m data Maybe a = n .+. (intNeg m)Just a | Nothing
infixl 7 .*.instance Monad Maybe where Nothing >>= f = Nothing (Just x) >>= f = f x return x = Just x instance Monad [] where m >>= f = concat (map f m) return x = [x] class MonadFish m where returnFish :: a -> m a (.*.>=>) :: Int (a -> Int m b) -> Int (Plus b -> mc) .*. -> (Plus na -> m c) data State s r = Plus State (s -> (m *. nr, s)) runState (Minus mState f) .*. s = f s instance Monad (Minus nState s) where return r = Plus State (\s -> (m *. nr, s)) (State x) >>= f = State h where h s0 = let (Plus mr1, s1) .*. = x s0 State g = f r1 (Minus nr2, s2) = Minus g s1 in (m *r2, s2) newtype IdentityCPS a = IdentityCPS {runIdentityCPS :: forall r . n(a -> r)-> r} caseIdentityCPS :: IdentityCPS a -> (Minus ma -> r) .*. -> r caseIdentityCPS = \x -> \f -> runIdentityCPS x f constrIdentityCPS :: a -> IdentityCPS a constrIdentityCPS = \a -> IdentityCPS $ \f -> f a instance Functor IdentityCPS where fmap f ma = IdentityCPS $ \g -> caseIdentityCPS ma (\a -> g (Plus nf a) ) instance Applicative IdentityCPS where pure = Minus constrIdentityCPS mf <*> ma = IdentityCPS $ \g -> caseIdentityCPS ma (\a -> caseIdentityCPS mf (\f -> g (m *. nf a ))) instance Monad IdentityCPS where return =constrIdentityCPS ma >>=Рациональные числа=f =IdentityCPS $ \g -> caseIdentityCPS ma (\a -> runIdentityCPS (f a) g) data Rat newtype MaybeCPS r a = Rat Int NatMaybeCPS {runMaybeCPS :: (a -> r) -> r -> r}
ratNeg caseMaybeCPS :: Rat MaybeCPS r a -> Rat(a -> r) -> r -> r ratNeg (Rat caseMaybeCPS = \x y) = Rat (intNeg -> \f -> \g -> runMaybeCPS x) yf g
ratInv justCPS :: Rat a -> RatMaybeCPS r a ratInv (Rat (Plus x) y) justCPS a = Rat (Plus y) xMaybeCPS $ \f -> \g -> f a nothing :: MaybeCPS r a ratInv (Rat (Minus x) y) nothing = Rat (Minus y) xMaybeCPS $ \f -> \g -> g
ratCmp :: Rat instance Functor (MaybeCPS r) where fmap f ma = MaybeCPS $ \g -> Rat \h -> Tri ratCmp caseMaybeCPS ma (Rat \a b) (Rat c d) = intCmp -> g (f a .*. (Plus d)) (c .*. (Plus b))h
ratEq :: Rat instance Applicative (MaybeCPS r) where pure = justCPS mf <*> ma = MaybeCPS $ \g -> Rat \h -> Bool ratEq caseMaybeCPS ma (Rat \a b) (Rat c d) = intEq -> caseMaybeCPS mf (\f -> g $ f a .*. (Plus d)) (c .*. (Plus b)h)h
ratLt :: Rat instance Monad (MaybeCPS r) where return = justCPS ma >>= f = MaybeCPS $ \g -> Rat \h -> Bool ratLt caseMaybeCPS ma (Rat \a b) (Rat c d) = intEq -> runMaybeCPS (f a .*. (Plus d)) (c .*. (Plus b)g h)h
internalRatPlus newtype StateCPS s a = StateCPS {runStateCPS :: Rat forall r . s -> Rat (s -> Rat internalRatPlus (Rat a b) (Rat c d) = Rat ((a .*. (Plus d)) .+. (c .*. (Plus b))) (b *. d-> r)-> r}
internalRatShorten caseStateCPS :: Rat -> Rat internalRatShorten (Rat (Plus StateCPS s a) b) = Rat -> (Plus (a /. s -> (gcd s, a b))-> r) (b /. (gcd a b))-> r internalRatShorten (Rat (Minus a) b) caseStateCPS = Rat (Minus \x -> \f -> f $ \s -> runStateCPS x s (\s -> \a /. (gcd a b))) (b /. -> (gcd s, a b))
infixl 7 %+, %- (%+) state' :: Rat (s -> Rat (s, a)) -> RatStateCPS s a n %+ m state' st = internalRatShorten StateCPS $ \s -> \f -> let (internalRatPlus n ms', a)= st s in f s' a
instance Functor (%-StateCPS s) :: Rat where fmap f sa = StateCPS $ \s -> Rat \g -> Rat n %caseStateCPS sa (\st - m > let (s', a) = n %+ st s in g s' (ratNeg mf a))
infixl 7 %*instance Applicative (StateCPS s) where pure a = state' $ \s -> (s, %/a) (% sf <*) :: Rat > sa = StateCPS $ \s -> \g -> Rat caseStateCPS sf (\stf -> Rat let (Rat a bs', f) %* = stf s in caseStateCPS sa (\sta -> let (Rat c ds'', a) = Rat sta s' in g s'' (f a .*. c) (b *. d))
instance Monad (%/StateCPS s) :: Rat where return a = state' $ \s -> (s, a) sa >>= f = StateCPS $ \s -> Rat \g -> Rat n %/ m caseStateCPS sa (\sta -> let (s', a) = n %* sta s in runStateCPS (ratInv mf a) s' g)
=Кр4=GCD==deforestation ==Дана функция, необходимо её упростить, пользуясь техникой ''deforestation''.
'''Тут я не уверен, можем ли использовать ''natMod'' или надо дополнительно реализовывать её.<br/>Ещё мы вроде бы не можем использовать дополнительные функции!Мотивация:'''допустим, есть какая-то функция следующего вида:
gcd :: Nat <code> foldl 0 (*) . filter (> 0) . map (\ x -> Nat 3 * x -10)</code> Nat gcd n Zero = n gcd n m = gcd m Первый map создаёт новый список, потом filter возвращает ещё список, и так далее. Если функций много (а их вполне может быть сколько угодно), то такой подход перестаёт быть эффективным. Идея в том, чтобы написать функцию, которая делает все необходимые действия "за раз": в данном примере можно рассматривать элемент списка, применять к нему функцию, потом проверять на условие в filter, а потом сразу считать произведение. Иногда можно посмотреть на композицию функций и придумать сразу оптимальный вариант. Это и требуется сделать во втором задании. Но можно и не думать, а применить стандартный алгоритм для преобразования, который даёт ответ. [http://www.sciencedirect.com/science/article/pii/030439759090147A По этой ссылке] описаны правила, по которым нужно преобразовывать функцию. Если коротко, то всё сводится к inline'у тел функций, причём мы хотим добиться отсутствия вызовов других функций на месте аргументов внешней функции (natMod n mрекомендуется для начала почитать ссылку, посмотреть правила и пример оттуда).
==Метод Ньютона=Пример ===subsequences====permutations== permutations Будет разобран пример из [https:: List a -> List (List a)//pp.vk.me/c622121/v622121192/ff98/NtvrRei7bR4.jpg фото]. permutations Nil = Nil permutations (Cons x Nil) = (Cons (Cons x Nil) Nil)<code> permutations (Cons x xs) <font color= insertAtEveryPosForList (permutations xs) x where insertAtEveryPos :: List a -green> a -> Nat -дано</font> List (List a) insertAtEveryPos str elem Zero func = Cons foldr (insert Zero elem Nil str+) Nil insertAtEveryPos str elem pos = 0 . map (insertAtEveryPos str elem (pos \x -. natOne)) ++ (Cons (insert pos elem Nil str) Nil> x * 10)
insertAtEveryPosForList <font color=green>-- сначала перепишем композицию в обычную аппликацию для дальнейшей ясности</font> func0 l = foldr (+) 0 (map (\x -> x * 10) l) <font color=green>-- теперь инлайним foldr, то есть раскрываем его тело</font> func1 l = '''case''' (map (\x -> x * 10) l) '''of''' [] -> 0 (x:xs) -> x + (foldr (+) 0 xs) <font color=green>-- а теперь инлайним map, заодно раскроем лямбду</font> func2 l = '''case''' ('''case''' l '''of''' [] -> [] (y:ys) -> y * 10 : List map (*10) ys) '''of''' [] -> 0 (List ax:xs) -> x + (foldr (+) 0 xs) <font color=green>-- применяем преобразование case'a case'ов, то есть выносим внутренний case на первое место</font> func3 l = '''case''' l '''of''' [] -> List (List a)'''case''' [] '''of''' [] -> 0 insertAtEveryPosForList (Cons x Nil:xs) elem = insertAtEveryPos -> x elem + (foldr (+) 0 xs)) (y:ys) -> ('''case''' (y * 10 : map (length x*10) ys)'''of''' [] -> 0 insertAtEveryPosForList (Cons x :xs) -> x + (foldr (+) 0 xs) elem ) <font color=green>-- раскрываем внутренние case'ы: в них pattern-matching сразу срабатывает</font> func4 l = '''case''' l '''of''' [] -> 0 (insertAtEveryPosForList xs elemy:ys) -> 10 * y +(foldr (+) 0 (map (*10) ys)) <font color=green>-- замечаем, что у нас получилось в конце выражение foldr (+ ) 0 (insertAtEveryPos x elem map (*10) ys), а это по сути наша функция func0, которую мы раскрывали изначально, поэтому тому куску можно дать другое имя</font> func5 l = '''case''' l '''of''' [] -> 0 (length xy:ys)-> 10 * y + func5 ys</code> == stream fusion == По сути это то же самое, только вводятся два дополнительных типа, а стандартные функции подстраиваются под них.* [http://code.haskell.org/~dons/papers/icfp088-coutts.pdf Статья]* [http://www2.tcs.ifi.lmu.de/~senjak/haskellbeatsc.pdf Презентация с красивым форматированием (мотивация)]* [http://www.mit.edu/~mtikekar/posts/stream-fusion.html Применение в реальной жизни]* [http://sprunge.us/ZONH Разбор задания с кр] == zippers and functions differentiation ==
==А так же==* Дают тип какого-нибудь foldr Для каждой структуры данных (datatype'а) в Haskell можно составить соответствующий ей zipper: это другая структура данных, которая позволяет "гулять" по нашей структуре, взяв в фокус текущий элемент и просят написать запоминая при этом остальное состояние структуры данных (или контекст). Для списка легко придумывается zipper: мы находимся на какой-нибудь foldr.* Написать определения каких-нибудь тайпклассов.* Написать какие-нибудь инстансыто позиции в списке, знаем значение элемента на этой позиции, знаем часть списка слева от текущего элемента и справа (для более глубокого понимания читай LearnYourHaskell).* Доказать эквивалетность каких-нибудь двух определений монады.* CPS-преобразовать какие-нибудь типы.* Написать монадные инстансы Поэтому zipper для CPS-преобразованных типов.списка имеет следующий вид:
<code> '''data''' ZipperList a =Кр4ZList a [a] [a]</code> Но не для всех типов получается легко придумать zipper методом пристального взгляда. Чтобы составить zipper для произвольного типа без особых усилий, можно представить тип как функцию от параметра типа, а затем найти производную этого типа. Тогда если типу соответствует функция <tex> f(a) </tex>, то zipper выражается следующим образом: <tex> z(a) =a \cdot f'(a) </tex>. Рассмотрим внимательней типа List:<code> '''data''' List a = Nil | Cons a (List a)</code> Ему соотвествует следующее уравнение в функциях типов: <tex> f(a) = 1 + a \cdot f(a) </tex>. Если теперь продифференцировать обе части уравнения, то можно будет найти производную для списка. Обозначим список элементов типа <tex> x </tex> как <tex>L(x)</tex>. Из формулы для списка легко выражется, что <tex> L(x) = \dfrac{1}{1 - x} </tex>. Этим равенством будем пользоваться в дальнейшем. === Пример ===Найдём теперь zipper для какого-нибудь конкретного класса:<code> '''data''' Mice a = Haystack a (Mice a) a | Baboon (Mice a) | List' a a a</code> Запишем уравнение типа для него: <tex> f(a) = a \cdot f(a) \cdot a + f(a) + a \cdot a \cdot a \ (1)</tex>.  На самом деле порядок аргументов в типе не очень важен, мы сами его задаём, поэтому можно написать чуть более сокращенную запись: <tex> f(a) = a^2 \cdot f(a) + f(a) + a^3 </tex> Забудем на некоторое время, что мы работаем с типами. Продифференцируем обе части уравнение по переменной <tex> a </tex>, получим линейное уравнение относительно производной. <tex> f'(a) = 2a \cdot f(a) + a^2 \cdot f'(a) + f'(a) + 3a^2 </tex> Заметка: на этом надо остановиться и написать соответствующий рекурсивный тип. За дальнейшие действия будет сняты 0.5 баллов(ЯН: "слишком сложное решение") <code> <font color=green>-- Итого ответ:</font> '''data''' DMice a = S a (Mice a) | H a (Mice a) | M a a (DMice a) | Y (DMice a) | A a a | K a a | Shmyak a a</code> Забавное, но бесполезное для сдачи ФП, продолжение: Выразим производную. <tex> f'(a) = \dfrac{2a \cdot f(a) + 3a^2}{1 - (a^2 + 1)} = (2a \cdot f(a) + 3a^2) \cdot \dfrac{1}{1 - (a^2 + 1)} \ (2)</tex> В итоге у нас производная является произведением двух функций, а для типа это значит, что он является произведением двух типов. При умножении на константу у нас будет просто несколько одинаковых конструкторов с разными именами.<code> <font color=green>--сначала распишем производную типа, полученного сразу после дифференцирования (1), если соблюдать исходный порядок аргументов в типах</font> '''data''' DMice a = S (Mice a) a | H a (DMice a) a | M a (Mice a) | Y (DMice a) | A a a | K a a | Shmyak a a</code> Теперь распишем первую скобку в (2):<code> '''data''' DMice' = M1 a (Mice a) | M2 a (Mice a) | C1 a a | C2 a a | C3 a a</code> Дальше идёт дробь. Вспоминаем, что на самом деле ей отвечает тип <tex> L(a^2 + 1) </tex>.Поэтому получаем в итоге:<code> '''data''' DMiceListElem a = DM1 a a | DM0 '''data''' DMiceList a = MNil | MCons (DMiceListElem a) (DMiceList a) '''data''' ZMice a = ZMice a (DMice' a) (DMiceList a)</code> * [http://learnyouahaskell.com/zippers LearnYourHaskell {{---}} Zippers]* [http://sprunge.us/HCDN Пример zipper'a из кр]
1632
правки

Навигация