Иногда грустно видеть, как 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
не является рекурсивным, и есть одно небольшое предостережение: стандартные алгоритмы обычно работают с полуоткрытыми интервалами, т. е. диапазоном, составленным из двух итераторов first
и last
, где first
включено в интервал, но last
находится после последнего элемента в интервале. интервал. Следовательно, чтобы получить тот же результат, вам пришлось бы вызывать его со вторым итератором на одну позицию дальше, чем с вышеупомянутой функцией:
std::reverse(s.begin()+3,s.begin()+8); // pass iterators to 4th and one past the 8th character
Полный онлайн-пример
person
463035818_is_not_a_number
schedule
14.04.2020
endl
- person 463035818_is_not_a_number   schedule 14.04.2020end
. - person user4581301   schedule 14.04.2020std::reverse
- это то, что я бы назвал странным;) - person 463035818_is_not_a_number   schedule 14.04.2020void
) и передает все по значению, поэтому функция с этим прототипом вообще ничего не может вернуть. - person john   schedule 14.04.2020