Страницы

среда, 6 декабря 2023 г.

Проверка пересечений ссылочных параметров в Oberon

В языке 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)

Но и происходят такие пересечения чаще от злой воли автора кода, чем от простой случайности.

Примечания:

[0] Параметры-значения будут ссылочными только для структур — массивов и записей.

Комментариев нет:

Отправить комментарий

Обработка ошибок

Тема корректной обработки ошибок в программе является довольно сложным вопросом в программировании. Отчасти от того, что и она сама являет...