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
.