1.5.3. ПЕРЕХВАТ НЕУСПЕХОВ

В языке Рефал Плюс имеется возможность проверить, закончилось ли вычисление некоторой тропы неуспехом и, в зависимости от этого, предпринять те или иные действия.

Отрицаниями условий называются тропы вида

# S R

где S - источник, а R - хвост. Если хвост R состоит из одной запятой, его разрешается опускать, в результате чего отрицание условия принимает вид # S.

С синтаксической точки зрения любое отрицание условия является хвостом. Вычисление таких хвостов производится следующим образом. Первым делом делается попытка вычислить источник S. Если в результате этого получается пустое объектное выражение, результатом всего хвоста является неуспех. Если же результатом вычисления источника S является неуспех, то вычисляется хвост R, и то, что получится, считается результатом всей конструкции.

Распутьями называются тропы вида

\{ Q1; Q2; ... Qn; }

где Q1, Q2, ..., Qn - некоторые тропы. С синтаксической точки зрения любое распутье является источником. Вычисление распутья происходит следующим образом. Первым делом делается попытка вычислить тропу Q1. Если в результате этого получается объектное выражение Oe, оно считается результатом всего распутья. Если же результатом вычисления тропы Q1 является неуспех, то делается попытка вычислить \{Q2; ..., Qn;}, и то, что получится, является результатом всего распутья. Таким образом, распутье "перехватывает" неуспехи. Если результатом вычисления всех троп является неуспех, то и результатом всего распутья является неуспех.

Рассмотрим, например, тропу

1 :: sX,

\{ sX : 0 = 1; sX : 1 = 0; }

Она вычисляется следующим образом. Первым делом выполняется определение локальной переменной 1 :: sX, в результате чего sX получает значение 1. После этого делается попытка вычислить первую тропу распутья, т.е. sX : 0 = 1. При попытке выполнить сопоставление sX : 0 выясняется, что сопоставление проходит неуспешно, поэтому результатом этой тропы является неуспех. Вследствие этого делается попытка вычислить следующую тропу

sX : 1 = 0

, в результате чего получается результат 0, который и является результатом вычисления всей тропы. В некоторых случаях, однако, полезным оказывается другой вариант распутья, имеющий следующий вид:

{ Q1; Q2; ... Qn; }

Его отличие от предыдущего варианта проявляется в том, что если результатом вычисления всех троп является неуспех, то результатом вычисления распутья является ошибка $error(Fname "Unexpected fail"), где Fname - имя функции, в которой находится распутье.

Таким образом, мы можем считать, что пара скобок \{ ... } "прозрачна" для неуспехов, в то время как пара скобок { ... } для них "непрозрачна", ибо неуспех не может "выскочить" из распутья { Q1; Q2; ... Qn; }.

Довольно часто встречаются тропы следующего вида:

S : Ve, \{Ve : Snt1; Ve : Snt2; ..., Ve : Sntn;}

S : Ve, {Ve : Snt1; Ve : Snt2; ..., Ve : Sntn;}

где Ve - некоторая e-переменная, которая больше нигде в определении функции не используется, а каждое из предложений Sntj имеет вид Pj Rj. Для таких троп предусмотрена, соответственно, следующая сокращенная форма записи:

S : \{Snt1; Snt2; ... Sntn;}

S : {Snt1; Snt2; ... Sntn;}

называемая выбором, при этом конструкции \{Snt1; Snt2; ... Sntn;} и {Snt1; Snt2; ... Sntn;} называются образцовыми распутьями.

Хвосты Rj, состоящие из одной запятой, разрешается опускать, в результате чего соответствующие предложения Pj Rj принимают вид Pj. Так, например, распутье

{ sX : 0 = 1; sX : 1 = 0; }

более кратко записывается в виде выбора

sX : { 0 = 1; 1 = 0; }

, а распутье

\{ sX : A,; sX : B,; }

может быть сокращено до

sX : \{ A; B; }

С синтаксической точки зрения, все выборы являются источниками, что позволяет записывать, например, присваивания следующего вида:

sX : { 0 = 1; 1 = 0; } :: sY = <"+" sX sY>

При этом сначала вычисляется источник

sX : {0 = 1; 1 = 0;}

, затем полученный результат присваивается переменной sY, после чего вычисляется тропа

= <"+" sX sY>.