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

После выполнения шага вычислений - это результаты вычислений. Результаты можно разделить на три группы:

значение, выдаваемое вызовом функции: оно замещает в поле зрения отработанный вызов функции;

побочные эффекты, разбросанные по структуре поля данных;

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

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

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

Для абстрактного вычислителя граф функциональных зависимостей естественно считать полем памяти функциональной программы, в котором выделено поле зрения: активные (либо активные и потенциально активные) вершины-функции со своими аргументами.

В практике реализации функциональных систем программирования имеется три варианта конкретизации представления графа:

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

Коммутационные схемы, которые строятся на основе разделения функций и данных: функции представляются вершинами графа, а их аргументы-данные передаются по дугам, соединяющим вершины. Дуги рассматриваются в качестве каналов связи. Функция активизируется, когда ее аргументы появляются в каналах.

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

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

Программа на языке lisp задается как список применений функций, часть из которых может вычисляться во время исполнения самой программы. Как правило, заданные в программе списки интерпретируются как применения функций и вычисляются, если другое не определяют ранее активированные функции (вы видели, что функция quote запрещает вычисление своего аргумента, функция setq запрещает вычисление лишь первого из двух аргументов, а функция setf заставляет вычислить первый аргумент лишь до стадии, когда получена ссылка на его значение). Любое выражение выдает значение, что используется, в частности, при диалоговой работе с lisp (на примере которой иллюстрируются понятия).

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

(block name e1 . . . en) (8.1)

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

(return-from name value) (8.2)

Этим lisp выгодно отличается от большинства языков, где такие структурные переходы либо неполны, либо некорректно реализованы.

Далее, блоком считается любое описание функции. Описание функции производится при помощи функции defun, которая, в свою очередь, определяется через примитивы function и lambda. Первый из них задает, что имя, являющееся его аргументом, рассматривается как функция (он часто сокращается в конкретном синтаксисе до #'), второй образует значение функционального типа. Имя функции является и именем функционального блока.

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

[1]> (defun fact (n) (if (= n 0) 1

(* (fact (- n 1)) n)))

FACT

[2]> (fact 40)

815915283247897734345611269596115894272000000000

[3]> ((lambda (x) (fact (* x x))) 5)

15511210043330985984000000

[4]> (setq g '(lambda (x) (fact (* x x))))

(LAMBDA (X) (FACT (* X X)))

К-во Просмотров: 258
Бесплатно скачать Статья: Функциональное программирование