开发者

Introducing a dummy argument via STL or Boost

开发者 https://www.devze.com 2023-03-02 19:26 出处:网络
Suppose one wants to fill a vector with random numbers. Then there is a following obvious solution: vector<int> result;

Suppose one wants to fill a vector with random numbers. Then there is a following obvious solution:

vector<int> result;
result.resize(n);
for (int i = 0; i < n; ++i) {
    result[i] = generateRandomNumber();
}

OK, it obviously works, but I would like to understand what is t开发者_JAVA技巧he simplest STL/Boost way to get rid of the for loop. It is tempting to use std::transform, but it takes a function with one argument. Is there any nice STL way to introduce a dummy argument in a function?


The C++ standard library has std::generate() and std::generate_n();

For example:

#include <iostream>
#include <cstdlib>
#include <algorithm>
#include <vector>
#include <iterator>
int generateRandomNumber()
{
    return std::rand();
}
int main()
{
    int n = 10;
    std::vector<int> result;
    generate_n(back_inserter(result), n, generateRandomNumber);
    copy(result.begin(), result.end(), std::ostream_iterator<int>(std::cout, " "));
    std::cout << '\n';
}

test: https://ideone.com/5xD6P

As for the second question, if I understand it correctly, is how to create a functor that takes an int argument, ignores it, and calls your int f()?

C++98 way is to actually write the whole functor:

struct IgnoreArgument
{
    typedef int(*fp_t)();
    fp_t fp;
    IgnoreArgument(fp_t f) : fp(f) {}
    int operator()(int) const { return fp(); }
};
...
transform(v.begin(), v.end(), v.begin(), IgnoreArgument(f));

test: https://ideone.com/DTsyl

C++11 way is to use a lambda expression

transform(v.begin(), v.end(), v.begin(), [](int){return f();});

test: https://ideone.com/nAPXI

And the C++98/boost way is to use boost::bind

transform(v.begin(), v.end(), v.begin(), boost::bind(f));

test: https://ideone.com/cvd88


Use generate_n with how many elements you want, along with a back_insert_iterator to the vector where you want them stored, and a pointer to the function that generates the numbers.

#include <vector>
#include <algorithm>


int generateRandomNumber()
{
    static int i = 0;
    return 42 + (i++);
}

int main()
{
    std::vector<int> vi;
    std::generate_n(back_inserter(vi), 10, &generateRandomNumber);    
}

Note that by using the back_insert_iterator as I do here, you do not have to pre-size the vector, which is kludgy at best.


The problem here is that transform just isn't the right choice for the task at hand. The intent of transform is to take some inputs, transform each in some prescribed way, and produce an output for each of those inputs.

In this case, you don't have any inputs. transform would make sense if the values in the vector were based (somehow or other) in values in some existing vector.

generate_n is really the right solution to the problem -- it's intended to invoke some function/functor N times, producing N results, and assigning those to the output iterator (and its successors) that you supply. Since it's intended to generate values (rather than transforming existing values) the function/functor doesn't take an input, and you don't have to provide "fake" input.

As far as the "dummy" argument goes, wanting/needing it is probably a pretty good sign that (as in this case) you're using the wrong algorithm, and just shouldn't do that.

You do, however, sometimes get into the reverse situation: you want to use an algorithm that doesn't supply an argument, but you want to be able to supply an argument. For example, let's assume you wanted to be able to set the numbers in your array to a random number, with some lower and/or upper bound specified by the user. In this case, you want to be able to specify the bound(s) that'll be passed to your random number function, but neither generate nor generate_n has any provision for doing so.

In this case, you have two possibilities. One is bind (originally boost::bind, but now included in C++11). What I prefer as a rule is to use a functor, and I pass the argument(s) to the ctor:

class gen_random { 
    int lower;
    int upper;
public:
    gen_random(int lower = 0, int upper = RAND_MAX) : lower(lower), upper(upper) {}

    int operator() { return rand_range(lower, upper);
};

int main() { 
    std::vector<int> rand_ints;

    std::generate_n(std::back_inserter(rand_ints), 10, gen_random(1, 6));
    return 0;
}
0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号