В языке Oberon можно передавать переменные через ссылочные параметры процедур — параметры-значения[0] и параметры-переменные. Это удобно, потому что позволяет передавать ссылки на данные без обязательного размещения их в динамической памяти. Если же данные всё равно динамические, существует гарантия неутекания значений указателей с возможным побочным доступом к данным уже после вызова процедуры.
Но иногда возникает потребность в проверке того, не являются ли однотипные параметры на самом деле одним и тем же элементом, что может вызвать затруднение, так как в отличии от указателей сравнения параметров-ссылок в Oberon не предусмотрено. А это может быть важно, если как минимум один параметр — переменный(VAR), и его изменение может привести к нежелательному изменению другого параметра.
PROCEDURE Reverse(VAR dest: ARRAY OF INTEGER; src: ARRAY OF INTEGER);
VAR i: INTEGER;
BEGIN ASSERT(LEN(dest) = LEN(src));
FOR i := 1 TO LEN(src) DO
dest[i - 1] := src[LEN(src) - i]
END
END Reverse;
После вызова такой процедуры с одним и тем же массивом в качестве исходного и целевого параметра его значение в общем случае окажется испорченным. Для выбора другой логики или остановки некорректного выполнения можно было бы проверять адреса с помощью SYSTEM:
ASSERT(SYSTEM.ADR(dest) # SYSTEM.ADR(src))
Но SYSTEM является опасным опциональным модулем, нежелательным для простого использования. И способ непереносим — он не пригоден, например, при трансляции в Java и JavaScript. Однако возможна безопасная и переносимая проверка, хотя и более изощрённая. Достаточно выявления связанного изменения параметров.
PROCEDURE CheckByChange*(c: ARRAY OF INTEGER; VAR d: INTEGER): BOOLEAN;
VAR i, save: INTEGER; same: BOOLEAN;
BEGIN
save := c[0];
i := 1 - c[0] MOD 2; (* безопасное гарантированное изменение *)
d[0] := i;
same := c[0] = i;
IF same THEN d[0] := save END
RETURN
same
END CheckByChange;
PROCEDURE Check*(c: ARRAY OF INTEGER; VAR d: ARRAY OF INTEGER): BOOLEAN;
RETURN
(LEN(d) = LEN(c)) & CheckByChange(c, d[0])
END Check;
Процедура, умеющая работать с совпадающими фактическими параметрами-массивами:
PROCEDURE Reverse(VAR dest: ARRAY OF INTEGER; src: ARRAY OF INTEGER); VAR i: INTEGER;
PROCEDURE Swap(VAR d: ARRAY OF INTEGER; i, k: INTEGER);
VAR t: INTEGER;
BEGIN
t := d[i]; d[i] := d[k]; d[k] := t
END Swap;
BEGIN ASSERT(LEN(dest) = LEN(src));
i := src[0]; dest[0] := 1 - i MOD 2;
IF src[0] # i THEN
dest[0] := i;
FOR i := LEN(src) DIV 2 TO 1 BY -1 DO
Swap(dest, i - 1, LEN(dest) - i)
END
ELSE
FOR i := 1 TO LEN(src) DO dest[i - 1] := src[LEN(src) - i] END
END
END Reverse;
Для менее явно выраженных пересечений этот способ не подходит из-за ресурсоёмкости:
PROCEDURE Do(VAR res: INTEGER; VAR data: ARRAY OF INTEGER);
...
Do(data[rand], data)
Но и происходят такие пересечения чаще от злой воли автора кода, чем от простой случайности.
Комментариев нет:
Отправить комментарий