1.1.4 Составные процедуры


Мы нашли в Лиспе некоторые из тех элементов, которые должны присутствовать в любом мощном языке программирования:

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

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

Для начала рассмотрим, как выразить понятие «возведения в квадрат». Можно сказать так: «Чтобы возвести что-нибудь в квадрат, нужно умножить его само на себя».

Вот как это выражается в нашем языке:

(define (square x) (* x x))

Это можно понимать так:

Здесь мы имеем составную процедуру (compound procedure), которой мы дали имя square. Эта процедура представляет операцию умножения чего-либо само на себя. Та вещь, которую нужно подвергнуть умножению, получает здесь имя x, которое играет туже роль, что в естественных языках играет местоимение. Вычисление этого определения создает составную процедуру и связывает ее с именем square1.

Общая форма определения процедуры такова:

(define ((имя) (формальные-параметры)) (тело))

(Имя) — это тот символ, с которым нужно связать в окружении определение процедуры. (Формальные-параметры) — это имена, которые в теле процедуры используются для отсылки к соответствующим аргументам процедуры. (Тело) — это выражение, которое вычислит результат применения процедуры, когда формальные параметры будут заменены аргументами, к которым процедура будет применяться2. (Имя) и (формальные-параметры) заключены в скобки, как это было бы при вызове определяемой процедуры.

Теперь, когда процедура square определена, мы можем ее использовать:

(square 21)

441

(square (+ 2 5))

49

(square (square 3))

81

Кроме того, мы можем использовать square при определении других процедур. Например, x 2 + y 2 можно записать как

(+ (square x) (square y)))

Легко можно определить процедуру sum-of-squares, которая, получая в качестве аргументов два числа, дает в результате сумму их квадратов:

(define (sum-of-squares x y)

(+ (square x) (square y)))

(sum-of-squares 3 4)

25

Теперь и sum-of-squares мы можем использовать как строительный блок при дальнейшем определении процедур:

(define (f a)

(sum-of-squares (+ a 1) (* a 2)))

(f 5)

136

Составные процедуры используются точно так же, как элементарные. В самом деле, глядя на приведенное выше определение sum-of-squares, невозможно выяснить, была ли square встроена в интерпретатор, подобно + и *, или ее определили как составную процедуру.

1. Заметьте, что здесь присутствуют две различные операции: мы создаем процедуру, и мы даем ей имя square. Возможно, и на самом деле даже важно, разделить эти два понятия: создавать процедуры, никак их не называя, и давать имена процедурам, уже созданным заранее. Мы увидим, как это делается, в разделе 1.3.2.
2. В более общем случае тело процедуры может быть последовательностью выражений. В этом случае интерпретатор вычисляет по очереди все выражения в этой последовательности и возвращает в качестве значения применения процедуры значение последнего выражения.

results matching ""

    No results matching ""