Home > Net >  How to make sure only one of the variables is set to true?
How to make sure only one of the variables is set to true?

Time:04-25

class Foo {
  final bool a;
  final bool b;
  final bool c;
  final bool d;

  Foo({
    this.a = false,
    this.b = false,
    this.c = false,
    this.d = false,
  }) : assert(a ^ b ^ c ^ d, 'Only one of them can be true.');
}

I want to make sure that only one of the properties a, b, c and d can be true. I tried a ^ b ^ c ^ d but it didn't work for this case.

Note: I'm not looking for doing it the lengthy way i.e. (a && !b && !c && !d) || (!a && b && !c && !d) ...

CodePudding user response:

A general technique would be to put your booleans into a List and then count the number of true elements. This also would allow you to check whether exactly a certain number are true, at least a certain number are true, or at most a certain number are true.

Here are a couple ways to implement that:

extension CountExtension<T> on Iterable<T> {
 /// Returns a count of the elements in `this` that are equal to [value].
 int countOf(T value) => fold<int>(0, ((sum, e) => sum   ((e == value) ? 1 : 0)));
}

T identity<T>(T object) => object;

void main() {
  var booleans = [true, false, true, false];
  print(booleans.countOf(true));
  print(booleans.where(identity).length);
}

(I expect something equivalent to countOf exists in a common package somewhere already, but I can't seem to find one at the moment.)

CodePudding user response:

Since you want it in a constructor assert, I'll assume you also want a constant expression (because otherwise [a, b, c, d].where((x)=>x).length == 1 will do, as @jamesdlin showed).

The shortest I can come up with using only the base Boolean operators is

  assert((a || b || c || d) && 
         !(a && b || a && c || a && d || b && c || b && d || c && d))

Not that great, quadratic in the number of variables.

Using ?/: can make it smaller:

  assert(a ? !(b || c || d) : b ? !(c || d) : c ^ d)

Also quadratic, just with a (significantly!) smaller constant factor.

If you look at a necessary binary decision diagram (like a binary decision tree, only it's a DAG) for the Boolean function, it's actually linear, so that suggests that you can make a linear expression with only Boolean operators, but it does so by sharing subtrees, which would mean that you need either more complicated control flow or local variables. With a let constructor, you could do:

let cd1 = c ^ d, 
    ncd = !c && !d, 
    bcd1 = b ? ncd : cd1, 
    nbcd = !b && ncd
in a ? nbcd : bcd1

which scales to a linear expression in an arbitrary number of variables (because of the sharing of ncd here, which becomes even more sharing with more variables). It's really the same as the ?/: version above with identical sub-expressions being shared. Sadly, Dart doesn't have a let construct that you can use in an assert, so that's just theoretical fun.

So, I too would go with arithmetic operations, because you are counting:

 assert((a ? 1 : 0)   (b ? 1 : 0)   (c ? 1 : 0)   (d ? 1 : 0) == 1)

That expression is simple, linear, and valid in a const constructor assert.

  •  Tags:  
  • dart
  • Related