Иногда грустно видеть, как C ++ используется для обучения всему, но не C ++. Следующее является своего рода экспериментом, чтобы увидеть, сможем ли мы как-то приблизиться к std::reverse
(алгоритм, который вы должны фактически использовать), фактически игнорируя требования вашей домашней работы и выполняя небольшие усваиваемые шаги.
Давайте начнем с небольшой вариации решения, представленного в этом ответе . Вместо передачи string
вместе с индексами мы можем использовать итераторы. Короче говоря, итераторы являются связующим звеном между алгоритмами и структурами данных, в частности контейнером . Они могут ссылаться на элементы в контейнере, точно так же, как индекс или указатель.
void reversing2(std::string::iterator first, std::string::iterator last) {
if (first >= last) return;
std::swap(*first,*last);
reversing2(++first,--last);
}
Итераторы могут быть разыменованы как указатели для получения ссылки на элемент (*first
и *last
). RandomAccessIterators можно увеличивать (++first
), уменьшать (--last
) и сравнивать (first >= last
), так же, как вы делаете это с индексами.
Следующий шаг трудный, потому что он требует еще большего количества рук. Обратите внимание, что кроме сигнатуры функции ничто в вышеприведенной функции на самом деле не зависит от first
и last
, являющихся итераторами для элементов в std::string
. Например, чтобы изменить подмассив int[]
, нужно изменить только подпись:
void reversing2(int* first, int* last) {
if (first >= last) return;
std::swap(*first,*last);
reversing2(++first,--last);
}
Это дает хорошую возможность связаться с шаблонами. Я знаю, что я совершаю небольшое преступление здесь, потому что я не могу дать подробное вступление, но только представлю вам очень узкий случай. Чтобы сделать один и тот же код пригодным для использования в разных контейнерах, нам просто нужно немного его изменить
template <typename IT>
void reversing(IT first,IT last) {
if (first >= last) return;
std::swap(*first,*last);
reversing(++first,--last);
}
Теперь его можно вызывать с любым RandomAccessIterator. Итак, это:
#include <string>
#include <iostream>
int main() {
std::string s{"Hello world"};
std::cout << s << '\n';
reversing2(s.begin()+3,s.begin()+7); // pass iterators to 4th and 8th character
std::cout << s << '\n';
reversing(s.begin()+3,s.begin()+7);
std::cout << s << '\n';
int x[]= {1,2,3,4,5,6};
reversing(&x[2],&x[5]); // pointers are iterators too
for (const auto e : x) std::cout << e;
}
будет производить такой вывод:
Hello world
Helow olrld
Hello world
126543
В конце концов, и это было всей мотивацией для предыдущего, мы можем видеть, что reversing
очень похож на std::reverse
. Конечно, std::reverse
не является рекурсивным, и есть одна небольшая оговорка: стандартные алгоритмы обычно работают с полуоткрытыми интервалами, ie диапазон, составленный из двух итераторов first
и last
, где first
включено в интервал , но last
- один за последним элементом в интервале. Следовательно, чтобы получить тот же результат, вам придется вызывать его со вторым итератором на одну позицию дальше, чем с вышеуказанной функцией:
std::reverse(s.begin()+3,s.begin()+8); // pass iterators to 4th and one past the 8th character
Полный онлайн пример