22/06/2017

Многие пишут программы и упускают из виду некоторые тонкости используемого языка программирования. А потом долго ломают голову, не понимая, что не работает. (Впрочем, есть люди, которые что-то пишут, вообще не используя голову, но такие именуются не программистами, а говнокодерами). Вот пример. Допустим, вы пишете на языке Паскаль такой код:

while (a[i]>0) and (x mod a[i] <> 4) do
    { что-то делать}

Вы наивно думаете, что условие в заголовке цикла будет вычисляться по краткой схеме, т.е., если a[i]=0, то, очевидно, условие ложно, и вторая часть (x mod a[i] <> 4) вычисляться не будет. Ваша уверенность основана на том, что ваш любимый компилятор именно так и делает. Но ВДРУГ выясняется, что, будучи откомпилированной в другой среде, программа подрывается как раз при вычислении второй части (я однажды встретился с таким явлением сам). Почему?

Потому что надо читать стандарт языка, а не только инструкцию к данному конкретному компилятору. Скажем, Модифицированное сообщение о популярном в прошлом языке Алгол-60 недвусмысленно требует, чтобы в любом выражении вычислялись все его составляющие, поэтому приведенный выше трюк не должен работать ни в каком случае. Стандарт языка Паскаль ISO 7185 более лоялен к разработчикам компиляторов, но совершенно нелоялен к их пользователям, т.е. к прикладным программистам. Этот документ оставляет порядок вычисления выражений целиком на усмотрение реализаторов. И если в одном случае фрагмент будет работать, а в другом - нет, то программа оказывает непереносимой. Я полагаю, что это недостаток стандарта. Но изменить его я не в силах, как не в силах заставить разработчиков компиляторов следовать какому-то одному пути. Но я в силах изменить свою программу и сделать её, хотя и ценой потери красоты и эффективности, независимой от деталей реализации.

В приведенном примере, вроде бы, напрашивается очевидное решение

while a[i] > 0 do
    if x mod a[i] <> 4 then
        { что-то делать}

но оно неправильное, поскольку нарушение второго условия должно закончить цикл, а в таком варианте не закончит. Надо извращаться так:

cont := true;
while (a[i] > 0) and cont do
    if x mod a[i] <> 4 then
        { что-то делать}
    else
        cont := false;

Получается грубо, некрасиво, но правильно при любом поведении компилятора.

Вообще, стандартный Паскаль содержит много неудачных решений. Видел это и его создатель Никлаус Вирт. В стандарте Модулы-2 - наследницы языка Паскаль - четко сказано, что не нужно вычислять второй операнд, если значение выражения понятно из первого. Именно, там написано:

If the left operand to a logical conjunction operator has the value false , the value of the Boolean infix operation shall be the value false , and the right operand shall not be evaluated. If the left operand to a logical conjunction operator has the value true , the value of the Boolean infix operation shall be the value of the right operand.

If the left operand to a logical disjunction operator has the value true , the value of the Boolean infix operation shall be the value true , and the right operand shall not be evaluated. If the left operand to a logical disjunction operator has the value false , the value of the Boolean infix operation shall be the value of the right operand.

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

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

Поклонники Фортрана тоже не должны обольщаться. В стандарте Фортрана-2008, например, сказано (эта фраза без изменений перешла еще из Фортрана-95):

It is not necessary for a processor to evaluate all of the operands of an expression, or to evaluate entirely each operand, if the value of the expression can be determined otherwise.

(Необязательно, чтобы процессор вычислял все операнды выражения или вычислял полностью каждый операнд, если значение выражения может быть определено иным способом)
.

Иными словами, реализаторы не обязаны вычислять все компоненты выражения, но имеют полное право делать это.

Так что, предохраняйтесь, и будет вам программистское счастье!

Профиль

waspagv: (Default)
DCS Foyle

March 2025

M T W T F S S
     12
3456789
10111213141516
17181920212223
242526272829 30
31      

Style Credit

Expand Cut Tags

No cut tags
Page generated 14/07/2025 09:12
Powered by Dreamwidth Studios