Jump to content
Mike Torrettinni

Boolean short-circuit with function calls

Recommended Posts

I have functions A, B and C, and I need to return True if any of the function calls returns True, while all functions have to executed.

 

In this example - as I would usually setup function calls:

IsAnyTrue returns True if A, B or C functions are True
A,B,C functions have to executed

function IsAnyTrue1: boolean;
begin
  Result := false;
  
  if A then
  	Result := True;
  
  if B then
  	Result := True;
  
  if C then
  	Result := True;
end;

 

I can shorten this with:

 

function IsAnyTrue2: boolean;
begin
  Result := false;
  
  Result := A or Result;
  Result := B or Result;
  Result := C or Result;  
end;

OR! I just realized, I can do it like this:

 

function IsAnyTrue3: boolean;
begin
  Result := false;
  
  Result := A or B or C or Result;  
end;

But! This will trigger 'boolean short-circuit' rule. In this case I would need to use {$B+} to force full boolean expression evaluation - all A, B and C functions to be executed always.

 

Are there any other ways how I can setup Result and always execute all functions?

Share this post


Link to post
10 minutes ago, Mike Torrettinni said:

Are there any other ways how I can setup Result and always execute all functions?

Your first two solutions are fine. Why try to be "clever"?

  • Like 1
  • Thanks 1

Share this post


Link to post
1 minute ago, Anders Melander said:

Your first two solutions are fine. Why try to be "clever"? 

I have nested search across a lot of fields (grouped in functions), and just trying to shorten all the code.

Just trying to see if there are other options I can explore with my examples.

Share this post


Link to post
1 minute ago, Mike Torrettinni said:

Just trying to see if there are other options I can explore with my examples.

Okay then. There's none that I can think of that doesn't fall into the "too clever" category so I would choose the solution that is the most human readable.

Share this post


Link to post
21 minutes ago, Mike Torrettinni said:

always execute all functions

 

function CompleteEval(const Values: array of Boolean): Boolean;

 

  • Like 1
  • Thanks 1

Share this post


Link to post
1 minute ago, Anders Melander said:

"too clever"

Yes, too clever code usually means I will not know what it does in a week. So, I try to avoid it.

Share this post


Link to post
3 minutes ago, FredS said:

 


function CompleteEval(const Values: array of Boolean): Boolean;

 

How would that work to retain Result value, if True?

Share this post


Link to post
45 minutes ago, Mike Torrettinni said:

How would that work to retain Result value, if True?

result:=CompleteEval([result, A, B, C]);

Share this post


Link to post
4 minutes ago, Vandrovnik said:

result:=CompleteEval([result, A, B, C]); 

With:

function CompleteEval(const aBooleanValues: array of Boolean): Boolean;
var i: Integer;
begin
  Result := false;
  for i := Low(aBooleanValues) to High(aBooleanValues) do
    if aBooleanValues[i] then
      Result := True;
end;

Right?

Share this post


Link to post
5 minutes ago, Mike Torrettinni said:

With:


function CompleteEval(const aBooleanValues: array of Boolean): Boolean;
var i: Integer;
begin
  Result := false;
  for i := Low(aBooleanValues) to High(aBooleanValues) do
    if aBooleanValues[i] then
      Result := True;
end;

Right?

 

Yes, I just would change it a little, so that it can work a bit faster:

function CompleteEval(const aBooleanValues: array of Boolean): Boolean;
 var i: Integer;
 begin
 for i := Low(aBooleanValues) to High(aBooleanValues) do
  if aBooleanValues[i] then begin
   Result := True;
   exit;
 end;
 Result := false;
end;

 

  • Thanks 1

Share this post


Link to post
1 minute ago, Vandrovnik said:

Yes, I just would change it a little, so that it can work a bit faster:


function CompleteEval(const aBooleanValues: array of Boolean): Boolean;
 var i: Integer;
 begin
 for i := Low(aBooleanValues) to High(aBooleanValues) do
  if aBooleanValues[i] then begin
   Result := True;
   exit;
 end;
 Result := false;
end;

 

Great, thanks!

Share this post


Link to post
Just now, Stefan Glienke said:

And what exactly is wrong or not understandable in a week with using {B+} and a simple one liner?

Oh, no, I was not referring to this solution. As soon as 'clever' came up, I thought of some complicated code that uses some sort of tricks that if not docummented thoroughly, I would not recognize next week.

 

This is good solution, but I never use these switches, so I'm afraid I would forget to use it in next example. CompleteEval seems better solution, at this moment.

Share this post


Link to post
33 minutes ago, Stefan Glienke said:

And what exactly is wrong or not understandable in a week with using {B+}

 

Nothing, but in the past too many directives where bad for the health of Error/Code insight.. sure that's much improved now 🙂

  • Like 1

Share this post


Link to post
2 minutes ago, FredS said:

Nothing, but in the past too many directives where bad for the health of Error/Code insight.. sure that's much improved now 🙂

Conditionals probably but certainly not switches.

Share this post


Link to post

Actually I like both examples:

function IsAnyTrue3: boolean;
begin
  Result := false;
  
  {$B+} // Force full boolean expression evaluation
    Result := A or B or C or Result;  
  {$B-} 
end;

and

function IsAnyTrue4: boolean;
begin
  Result := false;
  
  Result := CompleteEval([Result, A, B, C]);
end;

 

Edited by Mike Torrettinni

Share this post


Link to post

Introduce a Variant in the check. I was hitting my head for days when I finally found out that Delphi always evaluates a boolean fully, if a variant comparison is present.

Share this post


Link to post

If you use {$BOOLEVAL ON} and {$BOOLEVAL OFF} instead it will be a bit clearer what's going on.

 

19 minutes ago, Mike Torrettinni said:

Result := false;
Result := CompleteEval([Result, A, B, C]);

 

There's no point in passing Result along as a parameter. You're just doing an logical OR on the values in the list anyway.

- and I do think you're being too clever. It's not a solution I would allow.

  • Thanks 1

Share this post


Link to post
4 minutes ago, Anders Melander said:

If you use {$BOOLEVAL ON} and {$BOOLEVAL OFF} instead it will be a bit clearer what's going on.

Even better, yet.

 

5 minutes ago, Anders Melander said:

There's no point in passing Result along as a parameter. You're just doing an logical OR on the values in the list anyway.

You are right, always room for some tweaking. Thanks.

Share this post


Link to post
3 hours ago, Mike Torrettinni said:

I have nested search across a lot of fields (grouped in functions), and just trying to shorten all the code.

Just trying to see if there are other options I can explore with my examples.

How about something like this?

uses
  System.SysUtils;
  
function IsAnyTrue5: boolean;
const
  Funcs: array[0..2] of TFunc<Boolean> = (A, B, C);
var
  i: Integer;
begin
  Result := False;
  for i := Low(Funcs) to High(Funcs) do
    Result := Funcs[i]() or Result;
end;

 

Edited by Remy Lebeau
  • Like 1

Share this post


Link to post
9 minutes ago, Remy Lebeau said:

const Funcs: array[0..2] of TFunc<Boolean> = (A, B, C);

Interesting, but I assume this is only applicable to this exact example, right? Can this be used if A,B and C have arguments?

Share this post


Link to post

As you are always obfuscating your code with love, I can't imagine why didn't you go with Result := Ord(a) + Ord(b) + Ord(c) + Ord(Result) > 0;

Edited by Attila Kovacs
  • Haha 1

Share this post


Link to post
2 minutes ago, Attila Kovacs said:

As you are always obfuscating you code with love

Don't we all? 🙂 (well, only if boss allows, of course 🙂 )

 

2 minutes ago, Attila Kovacs said:

Result := Ord(a) + Ord(b) + Ord(c) + Ord(Result) > 0;

Interesting.

Share this post


Link to post
1 hour ago, Remy Lebeau said:

How about something like this?

That code does not compile, method references cannot be declared as consts. What you probably meant was this:

const
  Funcs: array[0..2] of function: Boolean = (A, B, C);

And yes, that only works if they are parameterless otherwise you would have to declare that differently - but again only works if they are have homogeneous signatures.

 

Talking about possibly overengineering: https://en.wikipedia.org/wiki/Specification_pattern

Edited by Stefan Glienke

Share this post


Link to post

I can’t see past

// must call all functions, defeat short circuit evaluation
aVal := A();
bVal := B();
cVal := C();
Result := aVal or bVal or cVal;

I don’t care for temporarily disabling short circuit evaluation. I’d rather have the predictability of one rule for expression evaluation. Mix and match adds an impedance to understanding for the reader. 

  • Like 3
  • Thanks 1

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×