Ввод фиктивного аргумента через STL или Boost

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

тест: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));
  

тест:https://ideone.com/DTsyl

Способ C 11 — использовать лямбда-выражение

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

тест:https://ideone.com/nAPXI

И способ C 98 / boost заключается в использовании boost::bind

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

тест:https://ideone.com/cvd88

Комментарии:

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