What is the most efficient way to iterate in [false, true]?

Viewed 603

I have a function foo that takes 3 booleans, and I would want to get the result of this function with every combination of bools.

Here is how I did it:

fn foo(b1: bool, b2: bool, b3: bool) -> bool {
    b1 && b2 || !b3
}

fn main() {
    for b1 in vec![false, true] {
        for b2 in vec![false, true] {
            for b3 in vec![false, true] {
                println!("{} {} {} -> {}", b1, b2, b3, foo(b1, b2, b3));
            }
        }
    }
}

Is there a more direct/ shorter way to do it than creating a vector and then iterate through it?

Is there a way to do it with macros or with mutable functions, so that the compiler just go through each case without iterating?

4 Answers

You could use the itertools crate and then use the iproduct!() macro.

use itertools::iproduct;

fn main() {
    const FT: &[bool; 2] = &[false, true];

    for (&b1, &b2, &b3) in iproduct!(FT, FT, FT) {
        println!("{} {} {} -> {}", b1, b2, b3, foo(b1, b2, b3));
    }
}

Not saying it's "more efficient", but it is shorter to write.

At the very least you can use arrays instead of vectors and thus save the dynamic allocations:

fn foo(b1: bool, b2: bool, b3: bool) -> bool {
  b1 && b2 || !b3
}

fn main() {
  for b1 in &[false, true] {
    for b2 in &[false, true] {
      for b3 in &[false, true] {
        println!("{} {} {} -> {}", 
                 b1, b2, b3,
                 foo(*b1, *b2, *b3));
      }
    }
  }
}

Then you can save on loop nesting by using itertools::iproduct!:

use itertools::iproduct; // 0.9.0

fn foo(b1: bool, b2: bool, b3: bool) -> bool {
  b1 && b2 || !b3
}

fn main() {
  for (&b1, &b2, &b3) in iproduct!(&[false, true], &[false, true], &[false, true]) {
    println!("{} {} {} -> {}", 
             b1, b2, b3,
             foo(b1, b2, b3));
  }
}

(playground)

Ideally you'd write for b1 in [false, true], but that's not yet possible.

Even without it, you can do better than allocating and consuming a Vec - for example, you can iterate over an array slice:

for &b1 in &[false, true] {
    for &b2 in &[false, true] {
        for &b3 in &[false, true] {
            ...

Note that the pattern specifies a reference (which dereferences the matched value) because iterating over a slice of T doesn't produce actual T values, but &T references to values that reside in the slice.

To iterate directly over a fixed small number of values, you can use std::iter::once() and chain():

for b1 in std::iter::once(false).chain(std::iter::once(true)) {
    ...

The above can be made slightly shorter by (mis)using Option, which is also iterable:

for b1 in Some(false).into_iter().chain(Some(true)) {
    ...

There's no need to overcomplicate things. You can just do:

fn main() {
    for i in 0..=0b111 {
        let a = (i & 1) == 1;
        let b = (i >> 1 & 1) == 1;
        let c = (i >> 2 & 1) == 1;
        println!("{} {} {} -> {}", a, b, c, foo(a, b, c));
    }
}
Related