2

I am trying to write a template-based function frequency that will return the count of the occurrences of an item in an array of items.

So far I have

#include <iostream>
using namespace std;

template <class T>
T frequency(T array[], T arraySize, T item) {

    int count = 0;

    for (int i = 0; i < arraySize; i++) {
        if (array[i] == item) {
            count++;
        }
    }

    return count;

}

int main() {

    // Testing template with int
    int intArray[10] = { 1, 2, 3, 3, 14, 3, 2, 7, 99, 2 };
    cout << "{ ";
    for (int i = 0; i < 10; i++) {
        cout << intArray[i] << " ";
    }
    cout << "}" << endl;
    cout << "Frequency of 3: " << frequency(intArray, 10, 3) << endl;
    cout << "Frequency of 2: " << frequency(intArray, 10, 2) << endl;
    cout << "Frequency of 99: " << frequency(intArray, 10, 99) << endl;

    // Testing template with double
    double doubleArray[10] = { 1.5, 2.2, 99.4, 132.11, 1.5, 2.22, 1.515, 66.2, 51.8, 34.0 };
    cout << "{ ";
        for (int j = 0; j < 10; j++) {
            cout << doubleArray[j] << " ";
        }
    cout << "}" << endl;
    cout << "Frequency of 1.5: " << frequency(doubleArray, 10, 1.5) << endl;
    cout << "Frequency of 2.2: " << frequency(doubleArray, 10, 2.2) << endl;
    cout << "Frequency of 100.1: " << frequency(doubleArray, 10, 100.1) << endl;


    return 0;
}

however, I get an error of "no matching function for call to 'frequency(double [10], int, double)'" towards when I try to print out the frequency of the doubles. I am unsure what I am doing wrong.

Thank you for any help!

zerrisk
  • 33
  • 1
  • 7

3 Answers3

1

frequency takes parameters of element of array and arraySize with the same type, i.e. T. But you pass arguments of that with different types, i.e. double vs int. Then the type deduction fails because T can't be deduced (determined).

According to your implemantaion, the type arraySize seems fixed, you can just declare it as std::size_t or int. And same for the return type. Their types won't change then shouldn't be declared with template parameters.

template <class T>
int frequency(T array[], std::size_t arraySize, T item) {

    int count = 0;

    for (std::size_t i = 0; i < arraySize; i++) {
        if (array[i] == item) {
            count++;
        }
    }

    return count;
}
songyuanyao
  • 169,198
  • 16
  • 310
  • 405
  • Since I am passing different arguments for different arrays (double, int, and wanting to add string), how should I go about this? – zerrisk Dec 08 '16 at 06:40
  • @zerrisk The type of element of array will change, but the type of `arraySize` and return type doesn't change, isn't it? Then you shouldn't declare them as template parameters. – songyuanyao Dec 08 '16 at 06:44
  • I just changed that, but a guideline for my project was to pass the arraySize into the function. – zerrisk Dec 08 '16 at 06:47
  • @zerrisk I'm not sure what the guideline mean, but it seems fine because you're passing it as function argument with type `std::size_t` (or you could use `int`). (Is the guideline relevant with template parameter? Or just talking about function parameter?) – songyuanyao Dec 08 '16 at 06:50
  • Thank you, also I was able to figure out how to use string as well by adding a second "class S" in template , but I'm not sure why that works. – zerrisk Dec 08 '16 at 06:51
  • No. You *don't* want a second template parameter here. Let's consider a nice simple (non-template), function that counts the occurence of a string in an array of strings: `size_t frequency_s( std::string array[], size_t array_size, std::string item)`. Notice how only the array and the item are declared as `std::string`? They are the parameters you want to template. `array_size` and the function return type are *always* going to be some sort of integer (and I would recommend `size_t` for this sort of thing - you avoid all sorts of problems with comparing unsigned and signed values). – Martin Bonner supports Monica Dec 08 '16 at 06:54
  • @Martin Bonner If I'm only using 1 template parameter, I'm still not sure why integers and doubles work for it, but not strings. I have it set up as `int frequency(T array[], int arraySize, T item)` – zerrisk Dec 08 '16 at 06:58
  • @zerrisk Yes technically you can use multiple template parameters, it depends on how you implement it. – songyuanyao Dec 08 '16 at 07:02
  • @zerrisk What do you mean not work for strings? How do you use it? – songyuanyao Dec 08 '16 at 07:02
  • `string stringArray[5] = { "hello", "hello", "code", "finals", "last" };` `cout << "Frequency of \"hello\": " << frequency(stringArray, 5, "hello") << endl;` – zerrisk Dec 08 '16 at 07:04
  • @zerrisk Try `frequency(stringArray, 5, string("hello"))`. `"hello"` is not of type `std::string`, it's `const char[6]` indeed, and then leads to the same issue as I explained in my answer (the 1st paragraph). – songyuanyao Dec 08 '16 at 07:05
  • Thank you so much! May I ask why it is considered a const char[6] and not a string type? I'm sorry, going from a Java class to this slightly confuses me with the syntax and reasoning behind it. – zerrisk Dec 08 '16 at 07:08
  • @zerrisk http://stackoverflow.com/questions/1287306/difference-between-string-and-char-types-in-c – Stargateur Dec 08 '16 at 07:10
  • @zerrisk Sure. It's just because the C++ standard says that (mostly inherited from C). See more details about [string literal](http://en.cppreference.com/w/cpp/language/string_literal). – songyuanyao Dec 08 '16 at 07:11
  • @zerrisk: You are doing two things here: You are passing around some random objects (in an array, and as a target), and you are counting things (total number of objects in the array, and number of times target found). `int` and `double` are perfectly valid objects, but they can also be used to count things (although that can get a little dodgy for large numbers with double - because of variable precision). strings on the other hand are still perfectly valid objects, but they are not nearly so good for counting. – Martin Bonner supports Monica Dec 08 '16 at 08:39
0

You should not use your template parameter with arraySize. Because you compare it with i, int i = 0; i < arraySize;. And the return type should not be T too because a double is not accurate don't use it like counter. Plus you do return count and you write int count = 0;

template <class T>
int frequency(T const array[], int arraySize, T const &item);

Be aware that the standard library has a template function to this purpose. In a production code, you should use std::count_if().

int count = std::count_if(intArray.begin(), intArray.end(), [](int i){ return i == 42; });
Stargateur
  • 24,473
  • 8
  • 65
  • 91
  • If this was real code, you wouldn't write `frequency` at all - you would write `std::count` directly. This is *clearly* an educational exercise, and the OP will be expected to implement the body of the function themselves. – Martin Bonner supports Monica Dec 08 '16 at 06:56
  • I think you should tell him about `std::count`, but to say "You should use ..." is wrong. He shouldn't (because that doesn't meet the requirements he has). If you had written "In production code, you should use..." then I would entirely agree. – Martin Bonner supports Monica Dec 08 '16 at 08:53
-1

You can crate frequency as, so that it can be used with other containers.

#include <iostream>
#include <vector>

using namespace std;

template <class outputIterator,class T>
int frequency(outputIterator b,outputIterator e ,const T& v) {

    int count = 0;

    while(b != e)
    {
        if(*b == v)
        {
            count++;
        }
        b++;
    }
    return count;

}

int main ()
{
  std::vector<int> first={1,1,2,3,4,5,5};
  std::cout<<frequency(first.begin(),first.end(),1)<<std::endl;

  int  arr[]={1,2,3,5,5,5};
  std::cout<<frequency(std::begin(arr),std::end(arr),5)<<std::endl;

  return 0;
}
user1438832
  • 493
  • 3
  • 10