Ремонт принтеров, сканнеров, факсов и остальной офисной техники


назад Оглавление вперед




[15]

Возобновления представляют будущие вычисления, как функцию от промежуточного результата к конечному результату. В выносимом возобновлении стиле вычисления построены из последовательностей вложенных возобновлений, заканчивающихся конечным возобновлением (часто идентификатором), которое создаёт конечный результат. С тех пор как возобновления являются функциями, представляющими будущее вычисления, обработка функций возобновления может достичь сложных обработок будущего вычисления, как, например, прерывание вычисления в середине, прерывание части вычислений, возобновление вычислений и чередование выполнения вычислений. Монада Continuation приспосабливает CPS к структуре монады.

2.10.3. Определение

- r - это конечный результирующий тип всего вычисления newtype Cont r a = Cont { runCont :: ((a -> r) -> r) }

instance Monad (Cont r) where

return a= Cont $ \k -> k a -- return a = \k -> k a

(Cont c) >>= f= Cont $ \k -> c (\a -> runCont (f a) k) - c >>= f

= \k -> c (\a -> f a k)

Монада Continuation представляет вычисления в выносимом возобновления стиле. Cont r a - это CPS вычисление, которое создает промежуточный результат типа а в пределах CPS вычисления, чей конечный результирующий тип - r.

Возвращаемая функция просто создаёт возобновление, которое передаёт значение. Оператор >>= добавляет связанную функцию к возобновленной цепочке.

class (Monad m) => MonadCont m where

callCC :: ((a -> m b) -> m a) -> m a

instance MonadCont (Cont r) where

callCC f = Cont $ \k -> runCont (f (\a -> Cont $ \ -> k a)) k

Класс MonadCont создает функцию callCC, которая создает возобновленный механизм выхода для использования монадами Continuation. Возобновления выхода позволяют вам прервать текущее вычисление и возвратить непосредственно значение. Они достигают похожего результата с throwError и catchError в монаде Error.

Функция callCC вызывает функцию с таким текущим возобновлением, как её аргумент (отсюда имя). Стандартная идиома, используемая callCC - это создание -выражений для того, чтобы назвать возобновление. Затем вызов названного возобновления везде в пределах его области будет избавлять от вычисления, даже если находится на много слоёв глубже во вложенных вычислениях.

ФП 02005-03 01

№ докум.

Копипова Фопмат


В добавлении к механизму вывода, предусмотренному callCC, монада Continuation может быть использована для выполнения других, более мощных обработок возобновления. Эти другие механизмы имеют довольно специализированное использование, однако, и злоупотребление ими может легко создать зверски затемнённый код; поэтому они не будут охвачены здесь.

2.10.4. Пример

Этот пример показывает фрагмент того, как работает возобновление выхода.

Функция в примере использует возобновления выхода для выполнения сложных преобразований над целыми числами.

Код, имеющийся в example18.hs

- Мы используем монаду Continuation, чтобы выполнить "выходы" из блоков

-- кода. Эта функция осуществляет сложную управляющую структуру для обработки

-- чисел:

-- Ввод (n) Вывод Показательный Список

0-9 n ничего

10-199 число цифр в (n/2) цифры из (n/2)

200-19999 n цифры из (n/2)

20000-1999999 (n/2) назад ничего

>= 2000000 сумма цифр из (n/2) цифры из (n/2)

определяет "exit1"

fun:: Int -> String

fun n= ("runCont1 id) $ do

str <- callCC $ \exit1 -> do -

when (n < 10) (exit1 (show n))

let ns = map digitToInt (show (n "div" 2))

n <- callCC $ \exit2 -> do -- определяет "exit2"

when ((length ns) < 3) (exit2 (length ns))

when ((length ns) < 5) (exit2 n)

when ((length ns) < 7) $ do let ns = map intToDigit (reverse ns) exit1 (dropWhile (==0) ns) -- освобождает 2 уровня return $ sum ns

return $ "(ns = " ++ (show ns) ++ ") " ++ (show n) return $ "Answer: " ++ str

ФП 02005-03 01

Лист 52

Копиоова Формат


3. МОНАДЫ В РЕАЛЬНОМ МИРЕ

3.1.Introduction

Part I has introduced the monad concept and Part II has provided an understanding of a number of common, useful monads in the standard Haskell libraries. This is not enough to put monads into heavy practice, however, because in the real world you often want computations which combine aspects of more than one monad at the same time, such as stateful, non-determistic computations or computations which make use of continuations and perform I/O. When one computation is a strict subset of the other, it is possible to perform the monad computations separately, unless the sub-computation is performed in a one-way monad.

Often, the computations cant be performed in isolation. In this case, what is needed is a monad that combines the features of the two monads into a single computation. It is inefficient and poor practice to write a new monad instance with the required characteristics each time a new combination is desired. Instead, we would prefer to develop a way to combine the standard monads to produce the needed hybrids. The technique that lets us do exactly that is called monad transformers.

Monad transformers are the topic of Part III, and they are explained by revisiting earlier examples to see how monad transformers can be used to add more realistic capabilities to them. It may be helpful to review the earlier examples as they are re-examined.

3.2.Combining monads the hard way

Before we investigate the use of monad transformers, we will see how monads can be combined without using transformers. This is a useful excercise to develop insights into the issues that arise when combining monads and provides a baseline from which the advantages of the transformer approach can be measured. We use the code from example 18 (the Continuation monad) to illustrate these issues, so you may want to review it before continuing.

3.2.1. Nested Monads

Some computations have a simple enough structure that the monadic computations can be nested, avoiding the need for a combined monad altogether. In Haskell, all computations occur in the IO monad at the top level, so the monad examples we have seen so far all actually use the technique of nested monadic computations. To do this, the computations perform all of their input at the beginning - usually by reading arguments from the command line - then pass the values on to the monadic computations to produce results, and finally perform their output at the end. This structure avoids the issues of combining monads but makes the examples seem contrived at times.

ФП 02005-03 01

№ докум.

Копипова Фопмат



[стр.Начало] [стр.1] [стр.2] [стр.3] [стр.4] [стр.5] [стр.6] [стр.7] [стр.8] [стр.9] [стр.10] [стр.11] [стр.12] [стр.13] [стр.14] [стр.15] [стр.16] [стр.17] [стр.18] [стр.19] [стр.20] [стр.21] [стр.22] [стр.23] [стр.24] [стр.25]