#c #boost #stl
#c #boost #stl
Вопрос:
Предположим, кто-то хочет заполнить вектор случайными числами. Тогда есть следующее очевидное решение:
vector<int> resu<
result.resize(n);
for (int i = 0; i < n; i) {
result[i] = generateRandomNumber();
}
Хорошо, это, очевидно, работает, но я хотел бы понять, какой самый простой способ STL / Boost избавиться от цикла for. Заманчиво использовать std::transform , но это принимает функцию с одним аргументом. Есть ли какой-нибудь хороший способ STL ввести фиктивный аргумент в функцию?
Ответ №1:
Стандартная библиотека 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> resu<
generate_n(back_inserter(result), n, generateRandomNumber);
copy(result.begin(), result.end(), std::ostream_iterator<int>(std::cout, " "));
std::cout << 'n';
}
Что касается второго вопроса, если я правильно понимаю, как создать функтор, который принимает аргумент 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));
Способ C 11 — использовать лямбда-выражение
transform(v.begin(), v.end(), v.begin(), [](int){return f();});
И способ C 98 / boost заключается в использовании boost::bind
transform(v.begin(), v.end(), v.begin(), boost::bind(f));
Комментарии:
1. Большое вам спасибо. Но вопрос о фиктивной переменной все еще открыт.
2. @Илья Разенштейн Не могли бы вы привести пример того, как такая переменная будет использоваться?
3. Предположим, у меня есть функция int f(), теперь я хочу создать функцию int g(int) { return f(); }, используя стандартные вещи.
4. Почему бы и нет
generate_n(back_inserter(result), n, std::rand);
?5. @Rob OP имел
generateRandomNumber()
, я предоставил ему образец содержимого только для того, чтобы иметь компилируемый пример.
Ответ №2:
Используйте 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, amp;generateRandomNumber);
}
Обратите внимание, что при использовании back_insert_iterator
, как я делаю здесь, вам не нужно предварительно изменять размер вектора, что в лучшем случае является запутанным.
Комментарии:
1. @Ilya: Тогда замените
back_inserter
в моем примере на.begin()
2. Спасибо, но, опять же, вопрос о фиктивной переменной все еще открыт. Было бы здорово выяснить это.
3. @Ilya: Тогда, может быть, я не понимаю вопроса? Какая фиктивная переменная?
Ответ №3:
Проблема здесь в том, что 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;
}