Подтвердить что ты не робот

Оценка Delphi 'AND' с 2 условиями

Мне пришлось забрать Delphi за недавнюю контрактную работу, которую я делаю, и одна из вещей, которую я хотел бы прояснить, - это выполнение логики в условном выражении, таком как if.

Я пришел из фона в C/С++ и на этих языках, как только инструкция if, как известно, терпит неудачу, остальная часть логики не выполняется. Например:

if (somefunc() == FALSE && anotherfunc() == TRUE)

В приведенном выше случае, если somefunc() возвращает TRUE, то anotherfunc() никогда не вызывается.

В Delphi из того, что я вижу до сих пор, это не так. Скорее, для

if (somefunc() = False and anotherfunc() = True) then

то, независимо от того, что возвращает somefunc(), вызывается anotherfunc().

Я читал разные книги Delphi и перечитывал некоторые условные главы и не могу вообще упоминать об этом поведении. Может ли кто-нибудь указать мне где-нибудь в Delphi или Pascal, где указано это поведение?

4b9b3361

Ответ 1

Ссылка здесь::

Булева оценка короткого замыкания

 
Type    Switch 
Syntax  {$B+} or {$B-} {$BOOLEVAL ON} or {$BOOLEVAL OFF} 
Default {$B-} {$BOOLEVAL OFF} 
Scope   Local 

Директива $B переключает между двумя различными моделями Delphi генерации кода для булевых операторов и и или.

В состоянии {$ B +} компилятор генерирует код для полного Boolean оценка выражений. Это означает, что каждый операнд булевой выражение, построенное из операторов and и or, гарантировано оценивается, даже когда результат всего выражения уже известно.

В состоянии {$ B-} компилятор генерирует код для короткого замыкания Булева оценка выражения, что означает, что оценка останавливается как вскоре, как результат всего выражения становится очевидным в правильный порядок оценки.

Как вы можете видеть, опция по умолчанию предназначена для оценки короткого замыкания.


К сожалению, вы немного перепутали свой тест. Ваш код Delphi на самом деле сильно отличается от кода C.

if (somefunc() == FALSE && anotherfunc() == TRUE)      // C code
if (somefunc() = False and anotherfunc() = True) then   // Delphi code

В Delphi оператор and имеет более высокий приоритет, чем оператор равенства =. Это означает, что ваш код Delphi эквивалентен:

if (somefunc() = (True and anotherfunc()) = True) then

Но в C и С++ приоритет - это наоборот. Таким образом, && имеет более низкий приоритет, чем ==. И поэтому аргументы Delphi и С++ if в вашем вопросе логически различны, независимо от оценки короткого замыкания.

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

if ((somefunc() = False) and (anotherfunc() = True)) then 

Это даст ту же логику, что и ваш код на С++, и вы бы увидели такое же поведение из-за короткого замыкания.

Наконец, вы никогда не должны тестировать False и True в Delphi. Всегда пишите код следующим образом:

if not somefunc() and anotherfunc() then 

Ответ 2

Если ваша функция anotherfunc() вызывается в этом коде

if (somefunc() = False and anotherfunc() = True) then

то вы установили BOOLEVAL ON

Как сказал Дэвид, компилятор сначала оценил False and anotherfunc()

В режиме BOOLEVAL OFF компилятор знает, что False and AnyBoolState приведет к False и поэтому anotherfunc() не вызывается (на самом деле он никогда не будет вызываться).

Как простой тест на то, что я расширил программу jachaguate, чтобы показать ваше выражение

program AndEvaluation;

{$APPTYPE CONSOLE}
{$R *.res}

uses
  System.SysUtils;

function FalseFunc( const AName : string ) : Boolean;
begin
  Write( AName, '(False)', '-' );
  Result := False;
end;

function TrueFunc( const AName : string ) : Boolean;
begin
  Write( AName, '(True)', '-' );
  Result := True;
end;

begin
  try

    // (somefunc() = False and anotherfunc() = True)
    //
    // in this testcase translated to:
    //
    // somefunc()    => FalseFunc( 'First' )
    // False         => FalseFunc( 'Second' )
    // anotherfunc() => TrueFunc( 'Third' )
    // True          => TrueFunc( 'Fourth' )

{$B+}
    Writeln( 'BOOLEVAL ON' );
    if ( FalseFunc( 'First' ) = FalseFunc( 'Second' ) and TrueFunc( 'Third' ) = TrueFunc( 'Fourth' ) )
    then
      Writeln( 'True' )
    else
      Writeln( 'False' );
{$B-}
    Writeln( 'BOOLEVAL OFF' );
    if ( FalseFunc( 'First' ) = FalseFunc( 'Second' ) and TrueFunc( 'Third' ) = TrueFunc( 'Fourth' ) )
    then
      Writeln( 'True' )
    else
      Writeln( 'False' );

  except
    on E : Exception do
      Writeln( E.ClassName, ': ', E.Message );
  end;

  ReadLn;

end.

И теперь давайте посмотрим на результат

BOOLEVAL ON
Second(False)-Third(True)-First(False)-Fourth(True)-True

BOOLEVAL OFF
First(False)-Second(False)-Fourth(True)-True

Как видно из вывода BOOLEVAL ON, ваш anotherfunc() будет называться до того, как вызывается somefunc().

С BOOLEVAL OFF ваш anotherfunc() называется никогда.

Если вы хотите иметь то же самое, что и

if (somefunc() == FALSE && anotherfunc() == FALSE)

вам нужно перевести его вот так

if ( somefunc() = False ) and ( anotherfunc() = False ) then

или лучший и более короткий путь

if not somefunc() and not anotherfunc() then

или, может быть, даже короче

if not( somefunc() or anotherfunc() ) then

Но чтобы избежать вызова anotherfunc() каждый раз, когда вам нужно установить BOOLEVAL OFF

Ответ 3

Чтобы избежать вызова функции, вы можете сделать это следующим образом:

  bool_somefunc := (somefunc() = 42);
  bool_anotherfunc := (anotherfunc() = 17);
  if ( (bool_somefunc = False) and (bool_anotherfunc = True) ) then

Это гарантирует, что каждый функтор называется либо коротким eval включается или выключается.