Передача аргументов

 

Один из аспектов нотации требует разъяснений: что происходит со значениями, переданными в качестве аргументов подпрограмме?

Рассмотрим вызов в форме

 

r (a1, a2, ..., an)

 

 

соответствующий программе

 

r (x1: T1, x2: T2, ..., xn: Tn) is ...

 

 

где r может быть как функцией, так и процедурой, и вызов может быть квалифицированным, как в b.r (...) . Выражения a1, a2, ..., an называются фактическими аргументами, а xi - формальными. (Помните, что для родовых параметров типа остается термин "параметр".)

Встают важные вопросы: каково соответствие между фактическими и формальными аргументами? Какие операции допустимы над формальными аргументами? Каково их влияние на соответствующие фактические аргументы?

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

 

x1 := a1; x2 := a2;... xn := an

 

 

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

[x]. Присваивание x значения в форме x := ...

[x]. Процедуры создания, где x является целью: create x.make (...)

Читатели, знакомые с механизмом передачи, известным как вызов по значению, поймут, что здесь ограничения более строгое: при вызове по значению формальные аргументы инициализируются значениями фактических, но затем могут быть целью любых операций.Ответ на третий вопрос - что может программа делать с фактическими аргументами? - вытекает из того, что присоединение используется для задания семантики связывания формальных и фактических аргументов. Присоединение (см. лекцию 8) означает копирование либо ссылки, либо объекта. Это зависит от того, являются ли соответствующие типы развернутыми:

[x]. Для ссылок (обычный случай) при передаче аргументов копируется ссылка, - Void , либо присоединенная к объекту.

[x]. Для развернутых типов (включающих основные типы INTEGER , REAL и т.п.), при передаче аргументов копируется объект.

В первом случае, запрет операций прямой модификации означает, что нельзя модифицировать ссылку (reference) через повторное присоединение или создание. Но если ссылка не пустая, то разрешается модифицировать присоединенный объект .

Рис. 13.1. Допустимые операции на аргументе ссылки

Если xi - один из формальных аргументов r , то тело программы может содержать вызов:

 

xi.p (...)

 

 

где p - процедура, применимая к xi , (объявлена в базовом классе типа Ti аргумента xi ). Процедура может модифицировать поля объекта, присоединенного к xi во время выполнения, то есть объекта, присоединенного к соответствующему фактическому аргументу ai .

Вызов q (a) никогда не может изменить значение a , если a развернутого типа и является объектом. Если же a является ссылкой, то ссылка не меняется, но объект, присоединенный к ней, может измениться в результате вызова.

Существует много причин, по которым не следует позволять программам прямую модификацию их аргументов. Одна из самых убедительных - Конфликтующие присваивания . Предположим, что язык допускает присваивания аргументам, и процедура[23]

 

dont_I_look_innocuous (a, b: INTEGER) is -- я выгляжу

-- безвредной, но не стоит мне доверять.

do

a := 0; b := 1

end

 

 

Теперь рассмотрим вызов dont_I_look_innocuous (x, x) . Каково значение x после возвращения: 0 или 1 ? Ответ зависит от того, как компилятор реализует изменения формальных - фактических аргументов при выходе программы. Это ставит в тупик не только программистов, использующих язык Fortran.

Разрешение программе изменять аргументы приводит к ограничениям на фактические аргументы. В этом случае он должен быть элементом, способным изменять свое значение, что допустимо для переменных, но не постоянных атрибутов (см. лекцию 18). Недопустимым фактическим аргументом становится сущность Current , выражения, такие как a + b . Устранение модификации аргументов позволяет избежать подобных ограничений и использовать любые выражения в качестве фактических аргументов.

Следствием этих правил является признание того, что только три способа допускают модификацию значения ссылки x : процедура создания create x... ; присваивание x := y ; и попытка присваивания x ?= y , обсуждаемая ниже. Передача x как фактического аргумента никогда не модифицирует x .

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

[x]. Использовать функцию, возвращающую объект с несколькими полями (обычно, возвращается ссылка на такой объект).

[x]. Использовать процедуру, изменяющую поля объектов соответствующих атрибутов. Затем клиент может выполнять запросы к этим полям.

Первый прием уместен, когда речь идет о составном результате. Например, функция не может возвращать два значения, соответствующих заглавию и году публикации книги, но может возвращать одно значение типа BOOK , с атрибутами title и publication_year . В более общих ситуациях применяются процедуры. Эта техника будет обсуждаться вместе с вопросом побочных эффектов в разделе принципов модульного проектирования[24].