Compare Boolean Variables in one statement as long as array C++
replace your definition by :
bool NANDGate(bool array[]){
bool at = array[0];
for(int i = 1; i < 5; i++){
at &&= array[i];
}
return !at;
}
The not (!
) must be made at the end to be compatible with !(array[0] && array[1] && array[2] && array[3] && array[4]);
And in your definition you also take into account some entries 2 times
But to do the && up to the end is useless, the best is just to do :
bool NANDGate(bool array[]){
for(int i = 0; i < 5; i++){
if (!array[i])
return true;
}
return false;
}
If you accept a C++17 solution, you can make all constexpr
using an helper function and template folding as follows
#include <iostream>
#include <utility>
#include <type_traits>
template <std::size_t N, std::size_t ... Is>
constexpr bool NANDhelper (bool const (&a)[N],
std::index_sequence<Is...> const &)
{ return ! (a[Is] && ...); }
template <std::size_t N>
constexpr bool NANDgate (bool const (&a)[N])
{ return NANDhelper(a, std::make_index_sequence<N>{}); }
int main ()
{
bool a[] { true, true, true, true, true };
bool b[] { true, false, true, true, true };
std::cout << NANDgate(a) << std::endl;
std::cout << NANDgate(b) << std::endl;
}
If you can't use C++17, but at least C++14, you can't use template folding but you can simulate it in an unused array initialization; something as follows
template <std::size_t N, std::size_t ... Is>
constexpr bool NANDhelper (bool const (&a)[N],
std::index_sequence<Is...> const &)
{
using unused = bool[];
bool val { true };
(void)unused { true, (val &= a[Is])... };
return ! val;
}
Unfortunately std::index_sequence
and std::make_index_sequence
are available only starting from C++14 so, if you want something similar, you have to emulate them (and NANDhelper()
can't be, in C++11, constexpr
).
You can also use std::all_of
with good readability as follows:
DEMO
!std::all_of(std::begin(boo), std::begin(boo)+5, [](bool b){ return b; });
If you want to define the function bool NANDGate(...)
with this STL function, then the following implementation well work for you:
DEMO
bool NANDGate(const bool *arr, std::size_t n)
{
return !std::all_of(arr, arr+n, [](bool b){ return b; });
}
Performance on GCC and Clang
I tested the performance of the above function (labeled as std::all_of
) and the accepted answer (labeled as Naive
) by Quick C++ Benchmark with both gcc-8.2 and Clang-7.0 in C++14 and O3 optimization.
The result is as follows.
The horizontal line represents the sizes of each boolean arrays.
In both compilers, std::all_of
shows better performance than the naive implementation for the sizes greater than ~8:
GCC (DEMO):
Clang (DEMO):
Looking at the source code of GCC, the reason of this result would be rather clear.
The current GCC implementation of std::all_of
can be seen in gcc/libstdc++-v3/include/bits/stl_algo.h and following one:
template<typename _InputIterator, typename _Predicate>
inline bool
all_of(_InputIterator __first, _InputIterator __last, _Predicate __pred)
{
return __last == std::find_if_not(__first, __last, __pred);
}
where std::find_if_not
is also implemented in the same file using a function __find_if
.
Note that there are two overload of __find_if
.
The first one is the very simple following one:
template<typename _InputIterator, typename _Predicate>
inline _InputIterator
__find_if(_InputIterator __first, _InputIterator __last,
_Predicate __pred, input_iterator_tag)
{
while (__first != __last && !__pred(__first))
++__first;
return __first;
}
OTOH, the second one is a overload function for the random access iterators and optimized for them.
The implementation is as follows.
Since the distance of the random access iterators is quickly calculated with constant complexity O(1), this manual loop-unrolling effectively works.
In our current case, boo
is a raw pointer that is a random access iterator.
Thus this optimized overload function is called.
This should be a reason why std::all_of
shows better performance than the naive implementation for almost all sizes:
DEMO (RAI ver. is called)
/// This is an overload used by find algos for the RAI case.
template<typename _RandomAccessIterator, typename _Predicate>
_RandomAccessIterator
__find_if(_RandomAccessIterator __first, _RandomAccessIterator __last,
_Predicate __pred, random_access_iterator_tag)
{
typename iterator_traits<_RandomAccessIterator>::difference_type __trip_count = (__last - __first) >> 2;
for (; __trip_count > 0; --__trip_count)
{
if (__pred(__first))
return __first;
++__first;
if (__pred(__first))
return __first;
++__first;
if (__pred(__first))
return __first;
++__first;
if (__pred(__first))
return __first;
++__first;
}
switch (__last - __first)
{
case 3:
if (__pred(__first))
return __first;
++__first;
case 2:
if (__pred(__first))
return __first;
++__first;
case 1:
if (__pred(__first))
return __first;
++__first;
case 0:
default:
return __last;
}
}
Although I don't know the details of the Clang implementations, that seems also to be optimized from the above plot. In addition, with the same reason, functions proposed by @0x0x5453 and @TobySpeight would also show better performance at least in these compilers.