3

I have a function which is evaluate multiple (7, in my case) boolean variables and conditions and the result is true if only one of them true (and the rest false of course). I have the following code:

function GetExclusiveTrue: boolean;
begin
  Result:= (
    Integer(BoolVar1) + 
    Integer(BoolVar2) + 
    Integer(BoolFunc3) + 
    Integer(BoolVar4) + 
    Integer(BoolFunc5) + 
    Integer(BoolVar6) + 
    Integer(BoolVar7)) = 1;
end;

I'm just wondering if there is any better solution than this?

PS: I think I haven't defined properly what is my problem.

I'm looking for a solution using logical operators only, without any casting involved.

PS2: Look like I can't explain properly what I'm looking for. I want to see a solution without iteration, selection, function calls, etc. ONLY boolean operators allowed. Why? I just want to know if that is possible or not. Looking for a combination of logical operations which provides the same result as the function above.

tcxbalage
  • 696
  • 1
  • 10
  • 30
  • 2
    Curious as I am, what is the real problem that you are solving and why are only logical operators allowed? Why is casting or bit operations not allowed? Does it have to be pure Delphi? How do you quantify "better"? – Tom Brunberg May 10 '15 at 06:20
  • When I say better, I mean logical operators only. I don't need solution with casting and bitwise operators, I already solved the task using type cast as you can see, and I could solve it with bitwise operators too. This one is kind of a theoretical question. Yes, must be written Delphi. – tcxbalage May 10 '15 at 09:12
  • 2
    FWIW, rather than casting you would use `ord()`. – David Heffernan May 10 '15 at 11:29

4 Answers4

4

Here's a function to calculate only-1-true, regardless of the number of booleans to check:

function GetExclusiveTrue(boolarray: array of Boolean) : Boolean;
var
  arrayindex  : integer;
begin
  result := false;
  for arrayindex := 0 to high(boolarray) do
    if boolarray[arrayindex] then
    begin
      result := not result;
     if not result then exit;
    end;
end;

First, assume a false result, then scan through the supplied array. Set the return value true on the first true found (if any) and clear the return value and exit if a second is found true.

This is a special case of counting how many are true:

function howmanytrue(boolarray: array of Boolean) : integer;
var
  arrayindex  : integer;
begin
  result := 0;
  for arrayindex := 0 to high(boolarray) do
    if boolarray[arrayindex] then inc(result);
end;

Obviously, GetExclusiveTrue = howmanyaretrue([your Booleans]) = 1 but this allows other questions like are none/all/all-but-1/majority/at-least-3/no-more-than-2/exactly-half true (assuming you know the number of Booleans you are examining.)

I tested this with a set of 11 checkboxes and 2 panels

procedure TForm1.CheckBoxClick(Sender: TObject);
begin
  Panel1.Caption := BoolToStr(GetExclusiveTrue([checkbox1.Checked,checkbox2.Checked,checkbox3.Checked,checkbox4.Checked,checkbox5.Checked,checkbox6.Checked,checkbox7.Checked,checkbox8.Checked,checkbox9.Checked,checkbox10.Checked,checkbox11.Checked]),true);
  Panel2.Caption := IntToStr(howmanytrue([checkbox1.Checked,checkbox2.Checked,checkbox3.Checked,checkbox4.Checked,checkbox5.Checked,checkbox6.Checked,checkbox7.Checked,checkbox8.Checked,checkbox9.Checked,checkbox10.Checked,checkbox11.Checked]));
end;
Magoo
  • 77,302
  • 8
  • 62
  • 84
4

I want to see a solution without iteration, selection, function calls, etc. ONLY boolean operators allowed. Why? I just want to know if that is possible or not. Looking for a combination of logical operations which provides the same result as the function above.

You want to implement this using only logical and, or, xor and not operators. That goes like this:

Result :=
     (b1 and not (b2 or b3 or b4))
  or (b2 and not (b1 or b3 or b4))
  or (b3 and not (b1 or b2 or b4))
  or (b4 and not (b1 or b2 or b3));

I gave an example with just four booleans but the concept is the same for any number.

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • If I understand correctly I have to test each of logical operands against the rest. Right? – tcxbalage May 10 '15 at 11:05
  • I can't see any way other than this of meeting you constraints. There are variants, but they are all of this basic form. For instance you could write the first line as `b1 and not b2 and not b3 and not b4`. But if you can only use logical operators this is all that is left. – David Heffernan May 10 '15 at 11:10
  • OK, your solution fulfil my question's requirement. – tcxbalage May 10 '15 at 11:14
  • @tcxbalage This seems to be the only solution, and the correct one, that answers your question. – Tom Brunberg May 10 '15 at 18:29
1

If you don't want to cast, then you can achieve the same thing this way:

function GetExclusiveTrue: boolean;
var
  Count: Integer;
begin
  Count := 0;
  if BoolVar1 then
    Inc(Count);
  if BoolVar2 then
    Inc(Count);
  if BoolFunc3 then
    Inc(Count);
  if BoolVar4 then
    Inc(Count);
  if BoolFunc5 then
    Inc(Count);
  if BoolVar6 then
    Inc(Count);
  if BoolVar7 then
    Inc(Count);
  Result := (Count = 1);
end;
Rob McDonell
  • 1,309
  • 9
  • 15
  • That's doing the same thing as the typecast, only using twice as many lines of code. – Ken White May 10 '15 at 03:22
  • 1
    I know, but the OP wanted "logical operators only, without any casting involved". Anyway, if you fold each "if" into a single line, then it's only a couple of lines longer :-) – Rob McDonell May 10 '15 at 03:25
  • Folding it into single lines simply makes it harder to read; it doesn't actually reduce the amount of code. I don't see any benefit to doing it this way over the way the OP is using; they're accomplishing the same thing - adding integer values and testing to see if the total equals 1. I didn't say you were wrong, and I didn't downvote; just mentioning it. – Ken White May 10 '15 at 03:27
  • And I never said that it was a better solution. I'm just giving an answer to the question in the terms it was asked. – Rob McDonell May 10 '15 at 03:33
  • 1
    Well the question is explicitly asking for a "better solution" than the one already in place. – Jerry Dodge May 10 '15 at 04:18
  • @JerryDodge This one is better than the original because of no casting. It is indeed not the best and none I would ever use or even think of. – Sir Rufo May 10 '15 at 04:34
  • @SirRufo Indeed, I was responding to the comment saying "I never said that it was a better solution" – Jerry Dodge May 10 '15 at 04:36
0

A better solution could be, you do a open array as

function GetExclusiveTrue(Values: array of Boolean ): Boolean;

and make a sum througth iteration.

NGLN
  • 43,011
  • 8
  • 105
  • 200
  • I wouldn't call this a "better" solution - I think OP is already using the better of the two options. – Jerry Dodge May 10 '15 at 01:23
  • Man, first arrays exists just for this, and in Delphi boolean type is Enum and you could not convert than for a Integer if the result of a function is boolean. I think is not a good practice make a code by this way, if after you need a more variables you will repeat code. – Wellington Ribeiro May 10 '15 at 01:48
  • I do understand your point, but the number of conditions is not changing in running time. Your solution more automatized, but my problem is the casting, instead of using only boolean operands. I think I haven't defined properly what is my problem, my fault. – tcxbalage May 10 '15 at 02:04
  • I understood the question - only one of the conditions must be true, if more than one are true, then return false. Correct? – Jerry Dodge May 10 '15 at 02:07