Muszę przyznać, że całkiem lubię rozwiązanie Potatoswatter ... całkiem.
Jak zademonstrował, nie jest to kwestia algorytmu, ale iteracji. Jednak jego rozwiązanie nie pasuje do rachunku, ponieważ testowanie dla iteracji end
jest bardzo trudne: wymaga dużej ostrożności przy przygotowywaniu pojemników i tworzeniu iteratorów, aby uniknąć nieokreślonego zachowania.
Myślę, że problem mógł być znacznie lepiej wyrażony za pomocą koncepcji, która jest dość podobna do iteratorów: widoków.
Widok to interfejs adaptera na jeden lub kilka kontenerów. W rzeczywistości nie zawiera niczego samego (to jest ważna część). Ma jednak interfejs podobny do interfejsu kontenera, dzięki czemu można kodować za pomocą zwykłych idiomów.
Piękne rzeczy na temat widoków, to fakt, że często można je komponować.
Na przykład, jeden bardzo prosty widok:
/// Only allow to see a range of the container:
/// std::vector<int> v(40, 3); // { 3, 3, 3, ... }
/// auto rv = make_range_view(v, 4, 5);
/// rv exposes the elements in the range [4,9)
/// in debug mode, asserts that the range is sufficiently large
template <typename Container>
class range_view
{
};
Na pytanie, czy raczej stworzyć interleave_view
:
/// Allow to interleave elements of 2 containers each at its own pace
/// std::vector<int> v(40, 3);
/// std::set<int> s = /**/;
///
/// auto iv = make_interleave_view(v, 3, s, 1);
/// Takes 3 elements from v, then 1 from s, then 3 from v, etc...
template <typename C1, typename C2>
class interleave_view
{
};
Tutaj kwestia iteratora końcowego jest w jakiś sposób złagodzony, ponieważ wiemy, Oba pojemniki i dzięki temu jesteśmy w stanie samodzielnie tworzyć iteratory, zapewniając odpowiednie parametry.
Oczywiście może stać się nieco bardziej żmudny, jeśli pojemniki nie oferują wydajnego elementu "rozmiaru" (list
nie, to O (n)).
Ogólna zasada jest taka, że widok zapewnia większą kontrolę (i pozwala na więcej sprawdzeń) i jest bezpieczniejszy w użyciu, ponieważ dokładnie kontrolujesz tworzenie iteratorów.
Należy zauważyć, że w C++ 0x typ interleave_view
byłby typowy dla nieograniczonej liczby sekwencji.
Co oznacza koniec c? Czy chcesz go mieć 4 4 8 ... 8 8 8 8 lub po prostu przestać się łączyć? To jest a.end() jak w twoim przykładzie? – dyp
Co się stanie, jeśli któryś wektor wejściowy nie ma wystarczającej liczby elementów? – AnT
STL już ma konwencję dla tego - co robi 'std :: transform', gdy któraś z sekwencji wejściowych ma za mało elementów? – MSalters