Опубликован: 19.03.2004 | Уровень: специалист | Доступ: платный
Лекция 12:

Управление процессами

< Лекция 11 || Лекция 12: 123 || Лекция 13 >
Аннотация: Рассматривается эффективное обобщение процесса информационной обработки, вытекающее из возможности отложенных действий (lazy evaluation), органически присущей функциональному программированию благодаря унификации представления данных и программ. Анализируются резервы производительности обобщенных процессов и методы динамической оптимизации вычислений, приводящие к смешанным и параллельным вычислениям.

Замедленные (ленивые) вычисления

Средства управления процессами в функциональном программировании изначально опираются на интуитивное представление о вычислении выражений, согласно которому функция применяется к заранее вычисленным аргументам.

Ради полноты пространства вычислений, гибкости программ и результативности процессов такое представление пришлось расширить и ввести категорию специальных функций, которые "знают", когда и что из их аргументов следует вычислить. Специальные функции могут анализировать и варьировать условия, при которых вычисление аргументов необходимо. Так используется возможность манипулировать данными, представляющими выражения, и явно определять в программах позиции обращения к интерпретатору. Эта возможность применялась для поддержки стандартной программотехники и традиционных форм конструирования функциональных объектов.

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

Результат управления проявляется в изменении некоторых оценок, например можно влиять на эффективность и надежность программ, обусловленную целостностью объемных, сложных данных, избыточностью вычислений, возможно, бесполезных выражений, необоснованной синхронизацией формально упорядоченных действий. Подобные источники неэффективности могут быть устранены достаточно простыми методами организации частичных вычислений с учетом дополнительных условий для их фактического выполнения, таких как достижимость или востребованность результата вычислений.

Любое очень объемное, сложное данное можно вычислять "по частям". Вместо вычисления списка

(x1 x2 x3 ... )

можно вычислить x1 и построить структуру:

(x1 ( рецепт вычисления остальных элементов))

Получается принципиальная экономия памяти ценой незначительного перерасхода времени на вспомогательное построение. Рецепт — это ссылка на уже существующую программу, связанную с контекстом ее исполнения, т.е. с состоянием ассоциативного списка в момент построения рецепта.

(DEFUN ряд_цел (M N) (COND ((> M N) NIL)
                           (T (CONS M (ряд_цел (1+  M) N)))))

 
(DEFUN сумма (X) (COND ((= X NIL) 0)
                       (T (+ (CAR X)( сумма (CDR X))))) )
Пример 12.1. Построение ряда целых от M до N с последующим их суммированием.

Введем специальные операции ||приостановка вычислений и @возобновление ранее отложенных вычислений. Избежать целостного представления ряда целых можно, изменив формулу:

(DEFUN ряд_цел (M N) (COND ((> M N) NIL)
                           (T(CONS M ( || (ряд_цел (1+ M) N))))))
 
(DEFUN сумма (X) (COND ((= X NIL) 0)
   (T (+ (CAR X)  ( сумма (@ (cdr X))))) ))

Чтобы исключить повторное вычисление совпадающих рецептов, в его внутреннее представление вводится флаг, имеющий значение Tистина для уже выполненых рецептов, Fложь для невыполненных.

Тогда в выражении (ALL (CONS { 1 | 2 } (|| (цел 3 100) ))) второй аргумент cons выполнится только для одного варианта, а для второго подставится готовый результат. Таким образом, рецепт имеет вид:

{ ( F e AL )
| ( T x ) },

где x = ( EVAL e AL ).

Это позволяет распространить понятие данных на бесконечные, рекурсивно-вычислимые множества. Например, можно работать с рядом целых, больших чем N.

(DEFUN цел (M) (CONS M ( || (цел (1+ M) ))))

Можно из организованного таким образом списка выбирать нужное количество элементов, например первые K элементов можно получить по формуле:

(DEFUN первые (K Int) 
         (COND ((= Int Nil) NIL)((= K 0) NIL)
               (T (CONS (CAR Int)
                        ( первые (1-  K) @ (CDR Int))) )) ))

Эффект таких приостанавливаемых и возобновляемых вычислений получается путем следующей реализации операций || и @:

||e = > (LAMBDA () e )
@e = > (e ),

что при интерпретации приводит к связыванию функционального аргумента с ассоциативным списком для операции || и к вызову функции EVAL для операции @.

Обычно в языках программирования различают вызовы по значению, по имени и по ссылке. Техника приостановки и возобновления функций может быть названа вызовом по необходимости .

В некоторых языках программирования, таких как язык SAIL, Hope и Haskell, отложенные или замедленные вычисленияlazy evaluation основная модель вычислений.

Наиболее частый вариант — приостановка аргументов всех определенных пользователем функций и операции CONS. В таком случае порождаются многократные приостановки, что требует итеративного возобновления до непосредственно исполняемого рецепта.

Более подробно о тонкостях определения ленивых вычислений рассказано в книге Хендерсона [3].

< Лекция 11 || Лекция 12: 123 || Лекция 13 >
Дарья Федотова
Дарья Федотова
Сергей Березовский
Сергей Березовский

В рамках проф. переподготовки по программе "Программирование"

Есть курсы, которые я уже прошел. Но войдя в курс я вижу, что они не зачтены (Язык Ассемблера и архитектура ЭВМ, Программирование на С++ для профессионалов). Это как?

Алина Ленкова
Алина Ленкова
Россия, Ставрополь, СФ МГУПИ, 2014
Валерий Ромашов
Валерий Ромашов
Россия