Подстановка определения предиката на место вызова

Пусть A(x: y) { S } - определение предиката на императивном расширении языка P, а A(e: z) - вызов предиката в теле некоторого другого предиката B. Здесь x, y, z обозначают списки переменных, а e - список выражений. Подстановка определения предиката на место вызова A(e: z) есть замена вызова следующей композицией:

| x | = | e |; { S }; | z | = | y | . (7.1)

Конструкции | x |, | z | и | y | являются мультипеременными, а | e | - мультивыражением (см. разд. 6.6). В результате подстановки переменные наборов x и y становятся локальными переменными определения предиката B, в котором находился вызов A(e: z). В результате подстановки не должно возникать коллизий с именами переменных предиката B. В общем случае проведению подстановки предшествует предварительное систематическое переименование переменных. Нетрудно показать, что исполнение последовательности (7.1) эквивалентно исполнению вызова A(e: z) в соответствии с операционной семантикой (4.14) и семантикой выражений как аргументов вызова, определенной в разд. 5.4.

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

Оператор присваивания вида | x | = | e | называется групповым оператором присваивания. В соответствии с операционной семантикой (см. разд. 6.6) вычисление выражений, входящих в набор e, проводится параллельно. Эффективная реализация такого параллелизма возможна на процессорах с архитектурой широких команд. На других архитектурах параллельная реализация неэффективна, и поэтому замена группового оператора набором обычных операторов присваивания становится неизбежной. Это преобразование называется раскрытием группового оператора присваивания. В общем случае раскрытие группового оператора возможно при использовании дополнительных промежуточных переменных. Например, раскрытие оператора |a ,b| = |b, a| реализуется операторами t = a; a = b; b = t с использованием дополнительной переменной t. В большинстве случаев раскрытие возможно без использования дополнительных переменных; иногда достаточно поменять местами операторы присваивания. Например, раскрытие |a, b| = |c, a| реализуется присваиваниями: b = a; a = c.

Пусть набор x состоит из переменных x1, x2, …, xn; а e определяет набор выражений e1, e2, …, en. Допустим, ej является переменной для некоторого j. В соответствии с соглашением об отсутствии коллизий переменная ej должна быть отлична от xj. В подавляющем большинстве случаев замена xj на ej дает эквивалентную программу. Данная замена (склеивание переменной xj с ej) уменьшает число переменных на единицу и позволяет удалить копирование xj = ej в составе группового оператора | x | = | e |. Склеивание переменных xj и ej корректно за исключением случая, когда xj перевычисляется внутри S (что возможно как результат склеивания xj с другими переменными (см. разд. 7.3)), а переменная ej используется не только в вызове A(e: z), но и после него. Имеется еще одно условие: до проведения замены xj на ej переменная ej не должна встречаться в измененной версии S в (7.1); последнее возможно в случае, когда проведено склеивание переменных xk и ek, причем ek совпадает с ej.

Аналогичным образом проводится склеивание результирующих переменных набора y с переменными набора z. Поскольку переменные, входящие в набор z, должны быть различными, то склеивание результирующих переменных всегда возможно. При склеивании результатов устраняется групповой оператор | z | = | y |. Отмеченные склеивания (замену xj на ej и yj на zj) можно не проводить, если склеиваемые переменные обозначены одинаковыми идентификаторами в исходной программе. Поэтому естественной является следующая стратегия именования в процессе программирования на языке P. Если имеется вызов A(e: z) и требуется запрограммировать определение предиката A, то в качестве результирующих параметров y в определении A выбираются переменные набора z. Переменные в составе набора e становятся соответствующими аргументами определения A с учетом ограничений, указанных выше.