Представление фиктивного аргумента через STL или Boost - PullRequest
4 голосов
/ 03 мая 2011

Предположим, каждый хочет заполнить вектор случайными числами Тогда есть следующее очевидное решение:

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

ОК, это, очевидно, работает, но я хотел бы понять, какой самый простой способ STL / Boost избавиться от цикла for. Заманчиво использовать std :: transform, но она принимает функцию с одним аргументом. Есть ли хороший STL способ ввести в функцию фиктивный аргумент?

Ответы [ 3 ]

5 голосов
/ 03 мая 2011

Стандартная библиотека C ++ содержит std::generate() и std::generate_n();

Например:

#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

Что касается второго вопросаЕсли я правильно понимаю, как создать функтор, который принимает аргумент int, игнорирует его и вызывает ваш int f()?

C ++ 98 способ фактически написать весь функтор:

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 способ заключается в использовании лямбда-выражения

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

test: https://ideone.com/nAPXI

Испособ C ++ 98 / boost - использовать boost::bind

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

test: https://ideone.com/cvd88

1 голос
/ 03 мая 2011

Проблема здесь в том, что transform просто не является правильным выбором для поставленной задачи. Цель transform состоит в том, чтобы взять некоторые входные данные, преобразовать каждый в некотором предписанном порядке и создать выходные данные для каждого из этих входных сигналов.

В этом случае не имеет каких-либо входов. transform имело бы смысл, если бы значения в векторе (так или иначе) основывались на значениях в некотором существующем векторе.

generate_n - действительно правильное решение проблемы - он предназначен для вызова некоторой функции / функтора N раз, выдачи N результатов и назначения их для выходного итератора (и его преемников), который вы предоставляете. Так как она предназначена для генерации значений (а не преобразования существующих значений), функция / функтор не принимает ввод, и вам не нужно предоставлять «поддельный» ввод.

Что касается «фиктивного» аргумента, то, если вы хотите / нуждаетесь в нем, это, вероятно, довольно хороший признак того, что (как в этом случае) вы используете неправильный алгоритм, и просто не должны этого делать.

Однако иногда вы попадаете в обратную ситуацию: вы хотите использовать алгоритм, который не предоставляет аргумент, но вы хотите иметь возможность предоставить аргумент. Например, предположим, что вы хотите иметь возможность установить числа в вашем массиве на случайное число с некоторой нижней и / или верхней границей, указанной пользователем. В этом случае вы хотите иметь возможность указать границы, которые будут переданы вашей функции случайных чисел, но ни generate, ни generate_n не имеют для этого никаких условий.

В этом случае у вас есть две возможности. Один из них - bind (первоначально boost::bind, но теперь включен в C ++ 11). Как правило, я предпочитаю использовать функтор, и я передаю аргумент (ы) в 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;
}
1 голос
/ 03 мая 2011

Используйте generate_n с тем количеством элементов, которое вы хотите, а также back_insert_iterator для вектора, в котором вы хотите их хранить, и указатель на функцию, которая генерирует числа.

#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);    
}

Примечаниечто, используя back_insert_iterator, как я делаю здесь, вам не нужно предварительно изменять размер вектора, что в лучшем случае является хитрым.

...