Средства управления процессами в функциональном программировании изначально опираются на интуитивное представление о вычислении выражений, согласно которому функция применяется к заранее вычисленным аргументам.
Ради полноты
Свойственная функциональному программированию тенденция к полномасштабному применению всех попадающих в
Любое очень объемное, сложное данное можно вычислять "по частям". Вместо вычисления списка
(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))))) )
|| — @ —
(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 ( второй аргумент
{ ( 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 для операции @.
Обычно в языках программирования различают вызовы по значению, по имени и по ссылке.
Наиболее частый вариант — . В таком случае порождаются многократные приостановки, что требует итеративного возобновления до непосредственно исполняемого рецепта.
Более подробно о тонкостях определения ленивых вычислений рассказано в книге Хендерсона [3].
Не всегда неопределенность части данных мешает организовать вычисление.
Рассмотрим
(if (< X Y) Z T)
или эквивалент
if X < Y then Z else T
Если X и Y не определены, но известно, что X лежит в интервале [1, 4], а Y в интервале [5, 6], то логическое выражение X<Y определено, и можно сделать вывод относительно выбора ветви условного выражения и, возможно, получить его значение.
Изучение смешанных вычислений может исходить из разных толкований понятия частичности, т.е. функций, определенных не на всей области их существования.
Первые работы Lombardi в этой области посвящены
В.Э. Иткин оценивал частичность как практичный
При подготовке программ на в качестве определенного значения. Так, при реализации Lisp 1.5 введено соглашение, что значение атома в списке свойств хранится упакованным в список
[1].
В работах по формальной семантике стандартных языков программирования принято сведение к неопределенности значений любых операций, зависящих от неопределенных данных.
На практике это приводит к необоснованным потерям части определенной информации и результатов.
A_1+...+A_100_000_000+неопределенность -> неопределенность
(A …) и (A F), где F —
Например, роль такой функции может сыграть запрос у пользователя дополнительной информации:
(A …) => (A . ( ||(READ)) )
В определении интерпретатора обработка неопределенностей сосредоточена в функции ERROR.
(DEFUN EVAL (e AL) … ((assoc e AL)(cdr (assoc e AL))) (T(ERROR '"неопределенная переменная")) … )
В определение функции ERROR можно включить обращение к READ, обрамленное сообщением о ситуации с информацией о контексте.
(DEFUN APPLY (f args AL)
…
((assoc f AL)(apply (cdr (assoc f AL))
(evlis args AL)AL))
(T (ERROR ‘"неопределенная функция"))
…
)
При отладке сложных комплексов часто неразработанные определения замещают временными "заглушками", которые помогают разобраться в будущей программе по частям. Такую работу можно стандартизировать заданием предварительных определений функций в виде отображения типа аргументов в тип результата. Соответственно, исполнение предопределенной таким образом функции можно интерпретировать как проверку аргументов на соответствие типу аргументов и выдачу в качестве результата вариантов значения, принадлежащего типу результата.
При небольшом числе значений заданного типа, например,
(COND (e r)(T g))
=> (assoc e (list (CONS T (EVAL r AL))
(CONS NIL (EVAL g AL))) )
Таким образом выполнятся обе ветви, их результаты ассоциируются с различными значениями заданного типа, что позволяет получить нужный результат, как только доопределится ранее не определенное значение. Это позволяет избежать повторного выполнения предшествующих вычислений, если их объем достаточно велик.
Применение библиотечных процедур, зависящих от слишком большого числа параметров, можно упростить для пользователя построением проекций на типовые комплекты трудно задаваемых параметров, понимаемых как определение режима работы процедуры.
(DEFUN f (x y z a b c … v t u) (g …)) (DEFUN Fi (x y z ) (f x y z ai bi ci … vi ti ui))
Примерно это и делает необязательный параметр вида $$\text{\}$$ optional в языке
Такое построение можно рассматривать как декомпозицию, разделение, сортировку на выполнимые и невыполнимые действия, при которой выполнимые действия в тексте определения замещаются их результатом, а невыполнимые преобразуются в остаточные, что все вместе образует проекцию процедуры на заданную часть ее параметров.
Многие выражения по смыслу используемых в них операций иногда определены при частичной определенности их операндов, что часто используется при оптимизации кода программ:
X * 0 = 0 CAR (A …) = A X*1 = X при любом X X-X = 0 X/X = 1 и т.п.
Представление функции в некоторых точках при отладке можно задать ассоциативной таблицей:
(SETQ f ‘((a1 . r1)(a2 . r2)(a3 . r3) …)) (DEFUN f (x) (assoc x f))
В такое точечное определение легко добавлять недостающие пары, соответствующие нужным демонстрационным тестам при
Итак, мы получили некоторое число схем, различных с точки зрения управления вычислениями, полезных в разных ситуациях:
Возможны и другие, обеспечивающие оптимизацию, компиляцию, предвычисления, макрогенерацию текста программы, что в перспективе может покрыть полное пространство обработки программ в рамках единой методики. Например, основой единого подхода может быть так называемый трансформационный подход, заключающийся в сведении смешанных вычислений к преобразованию программ посредством набора базовых трансформаций.
Полное представление об асинхронных процессах, их эффективности и проблемах организации дают работы по сетям Петри.
Заметное место среди языков функционального программирования занимают языки
организации распределенных и параллельных вычислений. Практики с большой похвалой отзываются о языке функционального программирования
Название языка расшифровывается как "Streams and Iterations in a Single Assignment Language", сам он представляет собой дальнейшее развития языка VAL, известного в середине 70-х годов. Среди целей разработки языка
Эти цели создателей языка
Начнем с примера программы:
1. Вычисление числа $$\pi$$ (пи).
For % инициирование цикла Approx := 1.0; Sign := 1.0; Denom := 1.0; i := 1 while i <= Cycles do % предусловие завершения цикла Sign := -Sign; % однократные Denom := Denom + 2.0; % присваивания Approx := Approx + Sign / Denom; % образуют i := i + 1 % тело цикла returns Approx * 4.0 % выбор и вычисление результата цикла end for
2. Это выражение также вычисляет число $$\pi$$ (пи).
for i in [1..Cycles/2] do
% пространство параллельно
% исполнимых итераций
val := 1.0/real(4*i-3) — 1.0/real(4*i-1);
% тело цикла, для каждого i
% исполняемое независимо
returns sum( val ) % выбор и свертка результатов
% всех итераций цикла
end for * 4.0 % вычисление результата
% выражения
Это выражение вычисляет сумму всех вычисленных значений val и умножает результат на 4.0.
3, 4. В for-выражениях операции dot и могут порождать пары индексов при формировании пространства итерирования:
for i in [1..2] dot j in [3..4] do
% для пар индексов [1,3] и
% [2,4]
returns product (i+j)
% произведение сумм
end for % = 24
for i in [1..2] cross j in [3..4] do
% для пар [1,3], [1,4], [2,3]
% и [2,4]
returns product (i+j)
% произведение сумм
end for % = 600
5. Итеративное for-выражение с обменом данными между итерациями:
for I := 1 while I < S do K := I; I := old I + 2; % значение из предыдущей итерации J := K + I; returns product(I+J) end for
Как это свойственно языкам функнционального программирования, for - генератор do - тело цикла и returns - формирователь возвращаемых значений.
function Sum (N); % Сумма квадратов result (+ ( sqw (1 .. N)));
Обычно рассматривают оптимизации, обеспечивающие устранение неиспользуемого кода, чистку циклов, слияние общих подвыражений, перенос участков повторяемости для обеспечения однородности распараллеливаемых ветвей, раскрутку или разбиение цикла, втягивание константных вычислений, уменьшение силы операций, удаление копий агрегатных конструкций и др.
Средства управления процессами в функциональном программировании изначально опираются на интуитивное представление о вычислении выражений, согласно которому функция применяется к заранее вычисленным аргументам.
Ради полноты
Свойственная функциональному программированию тенденция к полномасштабному применению всех попадающих в
Любое очень объемное, сложное данное можно вычислять "по частям". Вместо вычисления списка
(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))))) )
|| — @ —
(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 ( второй аргумент
{ ( 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 для операции @.
Обычно в языках программирования различают вызовы по значению, по имени и по ссылке.
Наиболее частый вариант — . В таком случае порождаются многократные приостановки, что требует итеративного возобновления до непосредственно исполняемого рецепта.
Более подробно о тонкостях определения ленивых вычислений рассказано в книге Хендерсона [3].
Не всегда неопределенность части данных мешает организовать вычисление.
Рассмотрим
(if (< X Y) Z T)
или эквивалент
if X < Y then Z else T
Если X и Y не определены, но известно, что X лежит в интервале [1, 4], а Y в интервале [5, 6], то логическое выражение X<Y определено, и можно сделать вывод относительно выбора ветви условного выражения и, возможно, получить его значение.
Изучение смешанных вычислений может исходить из разных толкований понятия частичности, т.е. функций, определенных не на всей области их существования.
Первые работы Lombardi в этой области посвящены
В.Э. Иткин оценивал частичность как практичный
При подготовке программ на в качестве определенного значения. Так, при реализации Lisp 1.5 введено соглашение, что значение атома в списке свойств хранится упакованным в список
[1].
В работах по формальной семантике стандартных языков программирования принято сведение к неопределенности значений любых операций, зависящих от неопределенных данных.
На практике это приводит к необоснованным потерям части определенной информации и результатов.
A_1+...+A_100_000_000+неопределенность -> неопределенность
(A …) и (A F), где F —
Например, роль такой функции может сыграть запрос у пользователя дополнительной информации:
(A …) => (A . ( ||(READ)) )
В определении интерпретатора обработка неопределенностей сосредоточена в функции ERROR.
(DEFUN EVAL (e AL) … ((assoc e AL)(cdr (assoc e AL))) (T(ERROR '"неопределенная переменная")) … )
В определение функции ERROR можно включить обращение к READ, обрамленное сообщением о ситуации с информацией о контексте.
(DEFUN APPLY (f args AL)
…
((assoc f AL)(apply (cdr (assoc f AL))
(evlis args AL)AL))
(T (ERROR ‘"неопределенная функция"))
…
)
При отладке сложных комплексов часто неразработанные определения замещают временными "заглушками", которые помогают разобраться в будущей программе по частям. Такую работу можно стандартизировать заданием предварительных определений функций в виде отображения типа аргументов в тип результата. Соответственно, исполнение предопределенной таким образом функции можно интерпретировать как проверку аргументов на соответствие типу аргументов и выдачу в качестве результата вариантов значения, принадлежащего типу результата.
При небольшом числе значений заданного типа, например,
(COND (e r)(T g))
=> (assoc e (list (CONS T (EVAL r AL))
(CONS NIL (EVAL g AL))) )
Таким образом выполнятся обе ветви, их результаты ассоциируются с различными значениями заданного типа, что позволяет получить нужный результат, как только доопределится ранее не определенное значение. Это позволяет избежать повторного выполнения предшествующих вычислений, если их объем достаточно велик.
Применение библиотечных процедур, зависящих от слишком большого числа параметров, можно упростить для пользователя построением проекций на типовые комплекты трудно задаваемых параметров, понимаемых как определение режима работы процедуры.
(DEFUN f (x y z a b c … v t u) (g …)) (DEFUN Fi (x y z ) (f x y z ai bi ci … vi ti ui))
Примерно это и делает необязательный параметр вида $$\text{\}$$ optional в языке
Такое построение можно рассматривать как декомпозицию, разделение, сортировку на выполнимые и невыполнимые действия, при которой выполнимые действия в тексте определения замещаются их результатом, а невыполнимые преобразуются в остаточные, что все вместе образует проекцию процедуры на заданную часть ее параметров.
Многие выражения по смыслу используемых в них операций иногда определены при частичной определенности их операндов, что часто используется при оптимизации кода программ:
X * 0 = 0 CAR (A …) = A X*1 = X при любом X X-X = 0 X/X = 1 и т.п.
Представление функции в некоторых точках при отладке можно задать ассоциативной таблицей:
(SETQ f ‘((a1 . r1)(a2 . r2)(a3 . r3) …)) (DEFUN f (x) (assoc x f))
В такое точечное определение легко добавлять недостающие пары, соответствующие нужным демонстрационным тестам при
Итак, мы получили некоторое число схем, различных с точки зрения управления вычислениями, полезных в разных ситуациях:
Возможны и другие, обеспечивающие оптимизацию, компиляцию, предвычисления, макрогенерацию текста программы, что в перспективе может покрыть полное пространство обработки программ в рамках единой методики. Например, основой единого подхода может быть так называемый трансформационный подход, заключающийся в сведении смешанных вычислений к преобразованию программ посредством набора базовых трансформаций.
Полное представление об асинхронных процессах, их эффективности и проблемах организации дают работы по сетям Петри.
Заметное место среди языков функционального программирования занимают языки
организации распределенных и параллельных вычислений. Практики с большой похвалой отзываются о языке функционального программирования
Название языка расшифровывается как "Streams and Iterations in a Single Assignment Language", сам он представляет собой дальнейшее развития языка VAL, известного в середине 70-х годов. Среди целей разработки языка
Эти цели создателей языка
Начнем с примера программы:
1. Вычисление числа $$\pi$$ (пи).
For % инициирование цикла Approx := 1.0; Sign := 1.0; Denom := 1.0; i := 1 while i <= Cycles do % предусловие завершения цикла Sign := -Sign; % однократные Denom := Denom + 2.0; % присваивания Approx := Approx + Sign / Denom; % образуют i := i + 1 % тело цикла returns Approx * 4.0 % выбор и вычисление результата цикла end for
2. Это выражение также вычисляет число $$\pi$$ (пи).
for i in [1..Cycles/2] do
% пространство параллельно
% исполнимых итераций
val := 1.0/real(4*i-3) — 1.0/real(4*i-1);
% тело цикла, для каждого i
% исполняемое независимо
returns sum( val ) % выбор и свертка результатов
% всех итераций цикла
end for * 4.0 % вычисление результата
% выражения
Это выражение вычисляет сумму всех вычисленных значений val и умножает результат на 4.0.
3, 4. В for-выражениях операции dot и могут порождать пары индексов при формировании пространства итерирования:
for i in [1..2] dot j in [3..4] do
% для пар индексов [1,3] и
% [2,4]
returns product (i+j)
% произведение сумм
end for % = 24
for i in [1..2] cross j in [3..4] do
% для пар [1,3], [1,4], [2,3]
% и [2,4]
returns product (i+j)
% произведение сумм
end for % = 600
5. Итеративное for-выражение с обменом данными между итерациями:
for I := 1 while I < S do K := I; I := old I + 2; % значение из предыдущей итерации J := K + I; returns product(I+J) end for
Как это свойственно языкам функнционального программирования, for - генератор do - тело цикла и returns - формирователь возвращаемых значений.
function Sum (N); % Сумма квадратов result (+ ( sqw (1 .. N)));
Обычно рассматривают оптимизации, обеспечивающие устранение неиспользуемого кода, чистку циклов, слияние общих подвыражений, перенос участков повторяемости для обеспечения однородности распараллеливаемых ветвей, раскрутку или разбиение цикла, втягивание константных вычислений, уменьшение силы операций, удаление копий агрегатных конструкций и др.
Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.