1

[This is for a project that has a length limit, hence the question]

I have a function meant to return how many variables passed are equivalent to zero.

int countZeros(int a, int b, int c, int d)
{
    int zeros = 0;
    if(a == 0)
    {
        zeros++;
    }
    if(b == 0)
    {
        zeros++;
    }
    if(c == 0)
    {
        zeros++;
    }
    if(d == 0)
    {
        zeros++;
    }
    return(zeros);
}

The code functions perfectly fine but is very long for what it is achieving. Is it possible to do the same thing using a for loop?

Ex: for each 0 in [data-set]{zeros++;}?

I realize I could use a for loop while calling the function to test each variable, but the function is called inside of a variable declaration:

addavg[x][y] = ((varA + varB + varC + varD + nums[x][y]) / (5 - (countZeros(varA, varB, varC, varD)))); 
David
  • 250
  • 4
  • 15

3 Answers3

5

How about a variadic function? It will take any number of ints.

This example uses fold-expressions to make the code shorter, and easier to read.

template <typename ...Ts> 
int countZeros(Ts ...ts)
{
   return ( !ts + ...);
}

Additionally, you have this example use case

addavg[x][y] = ((varA + varB + varC + varD + nums[x][y]) / (5 - (countZeros(varA, varB, varC, varD))));

You can use the same technique as above to write

template <typename ...Ts> 
int sum(Ts ...ts)
{
   return (ts + ...);
}

and then your example becomes

addavg[x][y] = ((sum(varA, varB, varC, varD) + nums[x][y]) / (5 - (countZeros(varA, varB, varC, varD))));
cigien
  • 57,834
  • 11
  • 73
  • 112
3

One simple option would be just

return ((a == 0) + (b == 0) + (c == 0) + (d == 0));

Depending on what the requirements of your project are. For a more general approach, have a look at using va_list to accept any number of integers into your function. In that case yes, you would want a loop.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
lxop
  • 7,596
  • 3
  • 27
  • 42
2

For your specific example, you could shorten it to something like this:

int countZeros(int a, int b, int c, int d)
{
    return int(a == 0) + int(b == 0) + int(c == 0) + int(d == 0);
}

Live Demo

However, a variadic template function would be a better way to go, then you can pass in as many parameters as you want.

@cigien's answer shows you how to use fold expressions, which were added in C++17. But if you are using C++11 or C++14 instead, you can still use a variadic template function, you would just have to loop over the parameters manually (see How can I iterate over a packed variadic template argument list?), eg:

template <typename... Args> 
int countZeros(Args... args)
{
    int zeros = 0;
    for(int i : {args...}) {
        zeros += int(i == 0);
    }
    return zeros;
}

Live Demo

Alternatively:

#include <array>
#include <algorithm>

template <typename... Args> 
int countZeros(Args... args)
{
    std::array<int, sizeof...(args)> a = {args...};
    return std::count_if(a.begin(), a.end(), [](int i){ return (i == 0); });
}

Live Demo

Alternatively:

// for when the template parameter pack is empty...
int doCountZeros() {
    return 0;
}

template <typename Arg, typename... Args>
int doCountZeros(Arg firstArg, Args... otherArgs) {
    return int(firstArg == 0) + doCountZeros(otherArgs...);
}

template <typename... Args>
int countZeros(Args... args) {
    return doCountZeros(args...);
}

Live Demo

Any of these approaches will happily handle your example of countZeros(varA, varB, varC, varD).


Your example also shows the summing of the 4 variables together. That can be handled with a function as well, eg:

int sum(int a, int b, int c, int d)
{
    return a + b + c + d;
}

Live Demo

Or better, via a variadic function. If you are not using C++17 fold expressions, you can loop through the parameters manually, similar to shown above (also see Add all parameters with parameter pack expansion), eg:

template <typename... Args> 
int sum(Args... args)
{
    int result = 0;
    for(int i : {args...}) {
        result += i;
    }
    return result;
}

Live Demo

Alternatively:

#include <array>
#include <numeric>

template <typename... Args> 
int sum(Args... args)
{
    std::array<int, sizeof...(args)> a = {args...};
    return std::accumulate(a.begin(), a.end(), 0);
}

Live Demo

Alternatively:

int doSum() {
    return 0;
}

template <typename Arg, typename... Args>
int doSum(Arg firstArg, Args... otherArgs) {
    return firstArg + doSum(otherArgs...);
}

template <typename... Args>
int sum(Args... args) {
    return doSum(args...);
}

Live Demo

Any of these approaches will let you use sum(varA, varB, varC, varD) where needed.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770