короткое замыкание и скобки - PullRequest
8 голосов
/ 26 августа 2011

Имеет ли значение, как я группирую подвыражения при работе с одним оператором короткого замыкания?

a && b && c && d
a && (b && (c && d))
(a && b) && (c && d)
((a && b) && c) && d

Являются ли вышеуказанные выражения эквивалентными?

Ответы [ 4 ]

5 голосов
/ 26 августа 2011

Сравнительно легко доказать эквивалентность для двух упрощенных случаев трех подвыражений:

a && (b && c)  -->  a && bc   // bc is a shorthand for b && c

Здесь a будет оцениваться первым. Если оно ложно, короткое замыкание помешает оценке bc. Если это правда, bc будет оцениваться, то есть b && c будет оцениваться. Если b равно false, c не будет оцениваться.

(a && b) && c  -->  ab && c   // ab is a shorthand for a && b

Здесь ab будет оцениваться первым. (То есть a && b оценивается первым. Если a ложно, короткое замыкание помешает оценке b. В противном случае ab приводит к b.) Если ab ложно, c выиграно не будет оцениваться.


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

int a(), b(), c(), d();

void e()
{
    a() && b() && c() && d();
}

void f()
{
    a() && (b() && (c() && d()));
}

void g()
{
    (a() && b()) && (c() && d());
}

void h()
{
    ((a() && b()) && c()) && d();
}

(я использовал код C вместо кода C ++ для предотвращения искажения имени.)

созданная сборка для e:

_e:
    // ... enter ...
    call    _a
    testl   %eax, %eax
    je  L1
    call    _b
    testl   %eax, %eax
    je  L1
    call    _c
    testl   %eax, %eax
    je  L1
    call    _d
    testl   %eax, %eax
    nop
L1:
    // ... leave ...

созданная сборка для f:

_f:
    // ... enter ...
    call    _a
    testl   %eax, %eax
    je  L4
    call    _b
    testl   %eax, %eax
    je  L4
    call    _c
    testl   %eax, %eax
    je  L4
    call    _d
    testl   %eax, %eax
    nop
L4:
    // ... leave ...

созданная сборка для g:

_g:
    // ... enter ...
    call    _a
    testl   %eax, %eax
    je  L7
    call    _b
    testl   %eax, %eax
    je  L7
    call    _c
    testl   %eax, %eax
    je  L7
    call    _d
    testl   %eax, %eax
    nop
L7:
    // ... leave ...

созданная сборка для h:

_h:
    // ... enter ...
    call    _a
    testl   %eax, %eax
    je  L10
    call    _b
    testl   %eax, %eax
    je  L10
    call    _c
    testl   %eax, %eax
    je  L10
    call    _d
    testl   %eax, %eax
    nop
L10:
    // ... leave ...

Как видите, кроме меток, сгенерированный код сборки полностью идентичен.

5 голосов
/ 26 августа 2011

Да, все эти выражения эквивалентны, включая их короткое замыкание.

Скобки изменяют порядок, в котором оцениваются отдельные && s. Однако, поскольку && всегда ассоциативно слева, члены всегда оцениваются в порядке слева направо. Поэтому, как только термин окажется ложным, остальные могут быть пропущены.

2 голосов
/ 26 августа 2011

Это свойство называется Ассоциативность .Из статьи Википедии :

В математике ассоциативность является свойством некоторых бинарных операций.Это означает, что в выражении, содержащем два или более вхождений в строке одного и того же ассоциативного оператора, порядок выполнения операций не имеет значения, если последовательность операндов не изменяется.Таким образом, перестановка скобок в таком выражении не изменит его значения.

Встроенный operator&& является полностью ассоциативным, и, следовательно, применимо вышеизложенное.

Этоне всегда так, например:

  • operator- обычно левоассоциативен , то есть a - b - c == (a - b) - c != a - (b - c)
  • возведение в степень вправоассоциативное , то есть a ** b ** c == a ** (b ** c) != (a ** b) ** c
  • перекрестное произведение неассоциативное , то есть (a x b) x c != a x (b x c) (и без скобок выражение даже не имеет смысла)

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

2 голосов
/ 26 августа 2011

В вашем примере скобки не имеют значения. Но это просто из-за природы && , где необходимо проверить все термины (если истина или если один из них ложный, то ложный).

В этом примере круглые скобки имеют большое значение:

(a && b) || (c && d) // either a & b are true, or c & d

a && (b || c && d) // a must be true, and either b or c & d

(a && b || c) && d // d must be true, and either c or a & b

И, конечно, поскольку логика отличается, короткое замыкание работает по-другому.
В первой строке, если значение равно false, оно будет продолжено до второго члена (c && d).
Во второй строке, если a равно false, он просто вернет false.

...