Reputation: 615
I have a problem here: I am to write a function that prints the elements of a vector recursively so no loop is allowed.
I tried this code but it crashes at runtime:
void print(const std::vector<int> ivec, std::vector<int>::const_iterator it) {
if (it == ivec.end())
return;
std::cout << *it++ << std::endl;
print(ivec, it);
}
int main(){
vector<int> v{
5, 7, 77, 23, 10, 81
};
print(v, v.begin());
}
If I run the program I get the assertion dialog!?
Upvotes: 1
Views: 4065
Reputation: 195
If you just need to print the vector, I think a much more elegant solution would be to use iterators.
#include <iostream>
#include <vector>
using namespace std;
void print_vector(vector<int>::iterator it, const vector<int>::iterator &end)
{
if(it == end) {
cout << '\n';
return;
}
cout << *it << " ";
print_vector(++it, end);
}
int main() {
vector<int> v = {1,2,3,4,5,6,7,8,9};
print_vector(v.begin(), v.end());
return 0;
}
If you want to reuse the function with other structures (perhaps to impress a friend or a teacher) you can use the templates.
#include <iostream>
#include <set>
#include <vector>
using namespace std;
template<class TContainer>
void print_structure(typename TContainer::iterator it, const typename TContainer::iterator end)
{
if(it == end) {
cout << '\n';
return;
}
cout << *it << " ";
print_structure<TContainer>(++it, end);
}
int main() {
vector<int> vi = {1,2,3,4,5,6,7,8,9};
print_structure<vector<int>>(vi.begin(), vi.end());
vector<double> vd = {1.2, 3.4, 5.6, 7.8, 9.0};
print_structure<vector<double>>(vd.begin(), vd.end());
set<int> si = {10, 10, 10, 10, 20, 20, 20, 20, 30, 30, 30};
print_structure<set<int>>(si.begin(), si.end());
set<double> sd = {10.10, 10.10, 20.20, 20.20, 30.30, 3.0};
print_structure<set<double>>(sd.begin(), sd.end());
return 0;
}
Does it look like a bazooka to kill mosquitoes? Sure it is! But it's pretty crazy yeah?!
Upvotes: 0
Reputation: 2660
No need to pass in two arguments to the print function. If the vector is zero-length, print nothing.
if the vector is length of 1, print that element.
if the vector is of length greater than 1, then print a smaller vector (recursively) that does not include the last character, and then print the last character.
yes, this will create a copy of the vector for each recursion, but I guess that feels more like recursion to me. Incrementing a pointer on each loop does not feel like recursion.
#include <iostream>
#include <vector>
void print(const std::vector<int> vec) {
if (!vec.size())
return;
else {
print(std::vector<int>(vec.begin(), vec.end() - 1));
std::cout << " " << *(vec.end() - 1);
}
}
int main(){
std::vector<int> v{
5, 7, 77, 23, 10, 81
};
print(v);
}
Upvotes: 0
Reputation: 3911
You have to pass the vector by reference so that to avoid multiple copies thus the iterator is guaranteed to be compared with the iterators of the same vector only not with others':
void print(const std::vector<int>& ivec, std::vector<int>::const_iterator it) {
if (it == ivec.end())
return;
std::cout << *it++ << std::endl;
print(ivec, it);
}
int main(){
vector<int> v{
5, 7, 77, 23, 10, 81
};
print(v, v.begin()); // ok
vector<int>::iterator it = v.begin();
auto v2{ v };
if (it == v.begin())
cout << "it = v.begin()" << endl;
if (it == v2.begin()) // causes crash
cout << "it =v2.begin()" << endl;
}
Upvotes: 0
Reputation: 118330
void print(const std::vector<int> ivec, std::vector<int>::const_iterator it) {
The ivec
parameter is passed by value. Both of these parameters are passed by value. Passing by value by means that inside the function these are copies of the original parameters.
Your main()
calls this recursive function passing it its vector and the beginning iterator of its vector. However because all parameters are passed by value, each recursive iteration of the function compares the iterator to the end()
of a completely different vector. Undefined behavior.
You obviously forgot to pass the vector by reference. The first parameter to should be const std::vector<int> &ivec
.
Upvotes: 3
Reputation: 10740
When you call print
, you pass the vector by value. This means that it creates an entirely new vector each time, but the iterator still comes from the original vector. Because the iterator comes from a different vector, the test it == ivec.end()
is always going to fail.
We can fix this just by passing ivec
by const reference:
void print(const std::vector<int>& ivec, std::vector<int>::const_iterator it)
{
if (it == ivec.end())
return;
std::cout << *it++ << std::endl;
print(ivec, it);
}
And the code works fine!
Upvotes: 1