Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Delphi 'AND' evaluation with 2 conditions

Tags:

delphi

I've had to pick up Delphi for a recent contract piece of work I am doing and one of the things I'd like someone to clarify is the execution of logic in a conditional statement such as an if.

I come from a background in C/C++ and in those languages, as soon as an if statement is known to fail, the rest of the logic is not executed. For example:

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

In the case above, if somefunc() returns TRUE then anotherfunc() is never called.

In Delphi from what I can see so far, this does not hold true. Rather, for

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

then, irrespective of what somefunc() returns, anotherfunc() will be called.

I've read various Delphi books, and reread some of the conditional chapters and can't find mention of this behaviour anywhere at all. Can anyone point me to somewhere in Delphi or Pascal where this behaviour is stated?

like image 955
nrjohnstone Avatar asked Feb 26 '13 08:02

nrjohnstone


2 Answers

The documentation link is here:

Boolean short-circuit evaluation

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

The $B directive switches between the two different models of Delphi code generation for the and and or Boolean operators.

In the {$B+} state, the compiler generates code for complete Boolean expression evaluation. This means that every operand of a Boolean expression built from the and and or operators is guaranteed to be evaluated, even when the result of the entire expression is already known.

In the {$B-} state, the compiler generates code for short-circuit Boolean expression evaluation, which means that evaluation stops as soon as the result of the entire expression becomes evident in left to right order of evaluation.

As you can see, the default option is for short-circuit evaluation.


Unfortunately you got a little mixed up in your test. Your Delphi code is in fact quite different from the C code.

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

In Delphi the and operator has a higher precedence than the equality operator =. Which means that your Delphi code is equivalent to:

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

But in C and C++, the precedence is the other way around. So && has lower precedence than ==. And so the Delphi and C++ if statements in your question are logically different, irrespective of short-circuit evaluation.

I'm quite sure that you really meant to write your Delphi code like this:

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

That would give the same logic as your C++ code, and you would have seen the same behaviour due to short circuit evaluation.

Finally, you should never test against False and True in Delphi. Always write the code like this:

if not somefunc() and anotherfunc() then 
like image 96
David Heffernan Avatar answered Nov 05 '22 02:11

David Heffernan


If your function anotherfunc() gets called on this code

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

then you have set the BOOLEVAL ON

As David pointed out the compiler first evaluate False and anotherfunc()

In BOOLEVAL OFF mode the compiler knows, that False and AnyBoolState will result in False and therefore anotherfunc() is not called (in fact it will never be called).

As a simple test on that I extended the jachaguate program to show your expression

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.

And now lets have a look at the result

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

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

As the output explains having BOOLEVAL ON your anotherfunc() will be called before somefunc() is called.

With BOOLEVAL OFF your anotherfunc() is never called.

If you want to have the same as

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

you have to translate it like this

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

or the better and shorter way

if not somefunc() and not anotherfunc() then

or maybe even shorter

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

But to avoid getting anotherfunc() called every time you have to set BOOLEVAL OFF

like image 22
Sir Rufo Avatar answered Nov 05 '22 03:11

Sir Rufo