Подтвердить что ты не робот

Std:: copy n элементов или до конца

Я хотел бы скопировать до N элементов.

template< class InputIt, class Size, class OutputIt>
OutputIt myCopy_n(InputIt first, InputIt last, Size count, OutputIt result)
{
    Size c = count;
    while (first != last && c > 0) {
        *result++ = *first++;
        --c;
    }
    return result;
}

Есть ли способ сделать это с помощью std-функций? Я также мог:

template< class InputIt, class Size, class OutputIt>
OutputIt myCopy_n(InputIt first, InputIt last, Size count, OutputIt result)
{
    if(std::distance(first, last) > count)
        return std::copy_n(first,count,result);
    return std::copy(first,last,result);
}

однако, кроме того, что он громоздкий, он пробегает диапазон дважды (расстояние, копия). Если я использую итератор преобразования или фильтр-итератор, это O (N) ненужные вызовы моей функции filter/transform.

template <class InputIt, class OutputIt>
OutputIt copy_n_max(InputIt begin, InputIt end, OutputIt last, size_t count)
{
    return std::copy_if(begin, end, last, 
                        [&count](typename std::iterator_traits<InputIt>::reference)
                        { return count--> 0; });
}

int main()
{
    std::vector<int> v({1,2,3,4,5,6,7,8,9}), out;
    copy_n_max(v.begin(), v.end(), std::back_inserter(out), 40);
    for(int i : out) std::cout <<i << " ,";
}

выходы 1,2,3,4,5,6,7,8,9,

однако это будет продолжаться до конца, а не отсчет времени. так что еще больше ненужных вызовов моей функции фильтра/преобразования...

4b9b3361

Ответ 1

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

std::vector<int> v1, v2;
std::copy_n(v2.begin(), std::min(NUM, v2.size()), std::back_inserter(v1));

Если у вас есть доступ только к итераторам, я не знаю, как это сделать, используя только функции std без вычисления расстояния. Это дешево для итераторов с произвольным доступом, но дубликаты работают для других типов.

std::vector<int>::iterator i_begin, i_end, o_begin;
std::copy_n(i_begin, std::min(NUM, std::distance(i_begin, i_end)), o_begin);

Ответ 2

Я бы пошел на что-то вроде этого:

template <class InputIt, class OutputIt>
OutputIt copy_n_max(InputIt begin, InputIt end, OutputIt last, size_t count)
{
    return std::copy_if(begin, 
                        end, 
                        last, 
                        [&count](typename std::iterator_traits<InputIt>::reference) -> bool 
                        {
                            if (count > 0)
                            {
                                --count;
                                return true;
                            }
                            return false;
                        });
}

Использование предиката copy_if для проверки того, было ли достаточно копирование этого ввода. Основное преимущество, которое я вижу здесь, не является дополнительным std::distance.

Пример Live в IDEOne

Ответ 3

Существует простой способ использовать std::copy_if -overload, добавленный С++ 11 для вашей задачи (нужны только InputIterators):

template< class InputIt, class Size, class OutputIt>
OutputIt myCopy_n(InputIt first, InputIt last, Size count, OutputIt result)
{
    return std::copy_if(first, last, result,
        [&](typename std::iterator_traits<InputIt>::reference)
        {return count && count--;});
}

BTW: он становится еще лучше в С++ 14, нет необходимости в переменной или таком сложном аргументе:

std::copy_if(first, last, result,
    [count = some_complicated_expression](auto&&) mutable
    {return count && count--;});

Ответ 4

Вы можете использовать copy_if с пользовательским предикатором, и он работает для более старых версий С++.

#include <algorithm>
#include <iostream>
#include <vector>
#include <iterator>


struct LimitTo
{
    LimitTo( const int n_ ) : n(n_)
    {}

    template< typename T >
    bool operator()( const T& )
    {
        return n-->0;
    }

    int n;
};

int main()
{
    std::vector< int > v1{ 1,2,3,4,5,6,7,8 };
    std::vector< int > v2;

    std::copy_if( std::begin(v1), std::end(v1), std::back_inserter(v2), LimitTo(3) );

    std::copy( std::begin(v1), std::end(v1), std::ostream_iterator<int>(std::cout,", ") );
    std::cout << std::endl;
    std::copy( std::begin(v2), std::end(v2), std::ostream_iterator<int>(std::cout,", ") );
    std::cout << std::endl;
}

В этом примере копируются n элементов, используя предикат LimitTo.