I am having trouble executing this block of code. The first base condition of this code(for searching a string in a list of strings) does not work. Thanks.
int string_check(list<string> l,list<string>::iterator it,string s)
{
if(it==l.end()) return 0;
if(*it==s) return 1;
return(string_check(l,++it,s));
}
You're passing the list by value, so l.end() is the end of a different list each time, and never the one that it came from.
Either pass the list by reference; or pass the end iterator rather than the list itself. That would be a more flexible solution, allowing you to decouple the function from a specific container type and support any range of input iterators:
template <typename InIter, typename T>
bool contains(InIter begin, InIter end, T const & value) {
if (begin == end) return false;
if (*begin == value) return true;
return contains(++begin, end, value);
}
Recursion is often a bad idea, as the stack is typically fairly small and causes horrible bugs if it overflows. Unless this is an exercise in implementing such a function, use iteration:
for (; begin != end; ++begin) {
if (*begin == value) return true;
}
return false;
or the standard library:
return std::find(begin, end, value) != end;
You need to write int string_check(const list<string>& l, const list<string>::iterator it&, const string& s) instead.
Else you're taking a value copy of the std::list and the iterator, so any modifications to them will not be reflected in the caller.
I'm passing the objects by constant reference. This helps program stability since the function body cannot modify the parameters passed. I'm also passing the string in this way too in order to prevent an unnecessary value copy.
I would define the function differently. It is enough to specify a pair of iterators that specify the target range in the list.
bool string_check( std::list<std::string>::const_iterator first,
std::list<std::string>::const_iterator last,
const std::string &s )
{
return ( first != last ) &&
( *first == s || string_check( ++first, last, s ) );
}
That is there is no need to pass also the list itself.
The same way you could define a template function that could deal with any type of the list. Only the name of the function you should select more suitable.
For example
template <class InputIterator, class T>
bool find( InputIterator first,
InputIterator last,
const T &value )
{
return ( first != last ) &&
( *first == value || string_check( ++first, last, value ) );
}
As for your function implementation then the first parameter that is the list shall be be declared as reference
list<string> &l
Moover it should be declared as a const reference
const list<string> &l
that the function could be called for constant list. In this case you have also to change the type of the iterator.
Related
I don't understand the answers to the following questions:
Write a C++ function find_elem that takes two iterators first and last of some
sequence of elements of type T and an object obj of type T. It returns the iterator to the first occurrence of obj in the range [first, last), or the iterator last if obj is not in the sequence.
This was the answer
template <typename It, typename T>
It find_elem(It first, It last, const T & obj) {
while (first != last && (*first) != obj) // I DON'T UNDERSTAND THIS LINE
++first;
return first;
}
I dont understand the following line while (first != last && (*first) != obj). Why is it (*first != obj) when the questions asks you to return the iterator with the first instance of obj. I also don't get the following line ++first as in why you are incrementing the iterator first
The ++first is executed by the while loop.
I would write use { } here to make it clearer:
while (first!=last && (*first)!=obj) {
++first;
}
So, the while loop checks if (*first)==obj. If not, then it moves to the next element in the list using ++first, which increments the iterator. Then it ends either when first==last (meaning that we have gone through the entire list), or when (*first)==obj, meaning that we found what we were looking for.
A copy of first is passed to the function. This means that the function can safely modify the variable by using it also to iterate the sequence.
It's just a concise alternative for the following code:
template <typename It, typename T>
It find_elem(It first, It last, const T & obj) {
It iterator = first;
while (iterator != last && (*iterator) != obj)
++iterator;
return iterator;
}
By the way... "generic templates" sounds strange, because templates are always generic. I suppose template template parameters could be called "generic templates", though.
I have this std::find_if() like method that returns found iterator (that matches "condition" functor).
template<class T, class Function>
typename std::set<T>::iterator setFindIf(set<T> set, Function condition) {
typename std::set<T>::iterator iterator = set.begin();
for (; iterator != set.end(); iterator++) {
cout<<"U";
if (condition(*iterator)) {
break;
}
}
return iterator;
}
And this line that calls it:
std::set<Order>::iterator it = setFindIf(orders, orderCustomerHasOpenOrder(id, ordNum));
I'm testing on an empty set, so this line (that comes right after the above line) should print '1':
cout<<(it==orders.end());
Why doesn't this work? when I add this line at the end of the setFindIf() method, it prints '1' as expected.
You're taking your set in by value. So it's a copy of the container that you passed in. Comparing those iterators is undefined behavior, because they belong to different containers. Pass the set in by reference instead.
I'm having a beginner problem:
bool _isPalindrome(const string& str)
{
return _isPalindrome(str.begin(), str.end()); // won't compile
}
bool _isPalindrome(string::iterator begin, string::iterator end)
{
return begin == end || *begin == *end && _isPalindrome(++begin, --end);
}
What am I doing wrong here? Why doesn't str.begin() get type checked to be a string::iterator?
Update: Better version:
bool BrittlePalindrome::_isPalindrome(string::const_iterator begin, string::const_iterator end)
{
return begin >= end || *begin == *(end - 1) && _isPalindrome(++begin, --end);
}
Assuming that you have a declaration of the second function before the first function, the main issue is that you are passing the strings by const reference.
This means that the only overloads of begin() and end() that you have access to are the const versions which return std::string::const_iterator and not std::string::iterator.
The convention for iterators is that the end iterator points one beyond the end of a range and is not dereferencable - certainly if you pass str.end() as the end parameter. This means that *begin == *end is not valid, you need to decrement end once first. You are also going to have an issue with ranges with odd numbers of elements. By doing ++begin and --end with no further checking your iterators may cross over in the recursion rather than triggering the begin == end condition.
Also note that for maximum portability, global identifiers shouldn't start with an underscore.
str.begin() is non-const, while the argument str is const.
You can either change the iterator-accepting method to accept const_iterators, or you can change the string-accepting method to accept a non-const string.
Or you could cast away str's const-ness, but that would be a patent Bad Idea TM.
(I would also parenthesize your return statement on the iterator-accepting method to make your intent more clear, but that's neither here nor there.)
As previously mentioned your iterators need to be constant iterators, but there's something else wrong with your algorithm. It works fine if you have a string of odd length, but do you see what happens when your string is even length? Consider the palindrome:
aa
Your algorithm will pass in an iterator pointing to the front and to the end. All's good, then it will go to the next level, and all will still be good, but it won't end. Because your first condition will never be true. You need to check not only if begin==end but if begin+1==end or begin==end-1 if you prefer. Otherwise you're iterators are going to be upset.
What error are you getting?
Have you tried this?
bool _isPalindrome(string::const_iterator begin, string::const_iterator end)
replace iterator by const_iterator
swap function definitions
decrement end
Code:
bool isPalindrome(string::const_iterator begin, string::const_iterator end)
{
return (begin == end || begin == --end ||
*begin == *end && isPalindrome(++begin, end));
}
bool isPalindrome(const string& str)
{
return isPalindrome(str.begin(), str.end());
}
You haven't declared the second function before calling it in the first function. The compiler can't find it and thus tries to convert str.begin() (string::iterator) into a const string &. You can move the first function behind the second function.
My problem is more complex than this, so I've narrowed it down to a very simple example that would show me enough to know how to handle the rest.
Say I have an input iterator. I want make a new input iterator derived from it, where each element is the combination of multiple sequential elements of the original input with the following pattern. The run length is encoded in the input sequence.
Input:
{ 1 1 2 3 4 4 6 7 8 9 ... }
Output:
{ (1) (3+4) (6+7+8+9) ... }
I was thinking a function like this could process a single element and increment the input begin iterator (passed by reference). There are a few questions in my comments, plus I'd like to know if there's a good way to do it for the entire stream of elements.
EDIT: I'm aware there's a bug in the call to std::advance where the tmp iterator is incremented to be exactly end, which would be valid for this code. Let's focus on the rest of my questions and I'll fix that. Edit 2: should be fixed now?
template<class TInputIterator, class TOutputIterator>
void process_single(TInputIterator& begin, TInputIterator end, TOutputIterator destination)
{
std::iterator_traits<TInputIterator>::value_type run_length = *begin;
++begin;
// is there a better way to specify run_length elements to accumulate() without having to call advance() here?
TInputIterator tmp(begin);
std::advance(tmp, run_length);
// Edited: this condition should work for the different kinds of iterators?
if ((end < tmp) || (std::distance(begin, tmp) != run_length))
throw std::range_error("The input sequence had too few elements.");
// std::plus is the default accumulate function
*destination = std::accumulate(begin, tmp, 0/*, std::plus<TInputIterator::value_type>()*/);
// should I use std::swap(begin, tmp) here instead?
begin = tmp;
}
Edit 3: In response to the answers, would this be better?
template<class TInputIterator, class TOutputIterator>
TInputIterator process_single(TInputIterator begin, TInputIterator end, TOutputIterator destination)
{
typedef std::iterator_traits<TInputIterator>::value_type value_type;
value_type run_length = *begin;
++begin;
value_type sum = 0;
while (run_length > 0 && begin != end)
{
sum += *begin;
++begin;
--run_length;
}
if (run_length)
{
throw std::range_error("The input sequence had too few elements.");
}
*destination = sum;
return begin;
}
template<class TInputIterator, class TOutputIterator>
void process(TInputIterator begin, TInputIterator end, TOutputIterator destination)
{
while (begin != end)
{
begin = process_single(begin, end, destination);
}
}
I would write this algorithm manually.
Firstly, the function does not accept an input iterator, because those don't support advance and distance.
Secondly, the error checking is off. If I'm not mistaken, the possibility of end < tmp means some undefined behaviour has been invoked. Imagine the container is a std::list. What would happen if you managed to advance beyong list.end()? But I think it would be undefined even with a vector or array (and MSVC++ would probably kick in with its iterator debugging before you).
So, to decode the whole sequence, I'd do something like this:
#include <iostream>
#include <algorithm>
#include <vector>
#include <stdexcept>
#include <iterator>
template <class InputIterator, class OutputIterator>
void decode(InputIterator start, InputIterator end, OutputIterator output)
{
typedef typename std::iterator_traits<InputIterator>::value_type value_type;
while (start != end)
{
value_type count = *start;
++start;
value_type result = value_type();
for (value_type i = value_type(); i != count; ++i, ++start) {
if (start == end) {
throw std::range_error("The input sequence had too few elements.");
}
result += *start;
}
*output = result;
++output;
}
}
int main()
{
try {
std::vector<int> v;
decode(std::istream_iterator<int>(std::cin), std::istream_iterator<int>(), std::back_inserter(v));
std::copy(v.begin(), v.end(), std::ostream_iterator<int>(std::cout, " "));
}
catch (const std::exception& e) {
std::cout << e.what() << '\n';
}
}
// is there a better way to specify run_length elements to accumulate() without having to call advance() here?
Not really.
// Edited: this condition should work for the different kinds of iterators?
if ((end < tmp) || (std::distance(begin, tmp) != run_length))
throw std::range_error("The input sequence had too few elements.");
The problem here is the < operator, which is only going to work for RandomAccessIterators. Why not just:
if (std::distance(tmp, end) < run_length)
?
// should I use std::swap(begin, tmp) here instead?
begin = tmp;
Nope.
EDIT: I'm aware there's a bug in the call to std::advance where the tmp iterator is incremented to be exactly end, which would be valid for this code. Let's focus on the rest of my questions and I'll fix that.
Incrementing to end is standard behavior for STL algorithms.
void process_single(TInputIterator& begin, TInputIterator end, TOutputIterator destination)
STL iterators aren't generally a good type to pass byref. Callers all too often want to preserve them after the call to your function. For example, passing byRef causes this not to compile:
std::vector<something> t;
std::vector<something> t2;
process_single(t.begin(), t.end(), std::back_inserter(t2))
(Many compilers will take it but it's not standard)
Better would be to pass the iterator byval and then return the new position at which you end your algorithm, to be more consistent with the rest of the STL. For example, see std::find().
Hope that helps....
I want to make a function which moves items from one STL list to another if they match a certain condition.
This code is not the way to do it. The iterator will most likely be invalidated by the erase() function and cause a problem:
for(std::list<MyClass>::iterator it = myList.begin(); it != myList.end(); it++)
{
if(myCondition(*it))
{
myOtherList.push_back(*it);
myList.erase(it);
}
}
So can anyone suggest a better way to do this ?
Erase returns an iterator pointing to the element after the erased one:
std::list<MyClass>::iterator it = myList.begin();
while (it != myList.end())
{
if(myCondition(*it))
{
myOtherList.push_back(*it);
it = myList.erase(it);
}
else
{
++it;
}
}
STL lists have an interesting feature: the splice() method lets you destructively move elements from one list to another.
splice() operates in constant time, and doesn't copy the elements or perform any free store allocations/deallocations. Note that both lists must be of the same type, and they must be separate list instances (not two references to the same list).
Here's an example of how you could use splice():
for(std::list<MyClass>::iterator it = myList.begin(); it != myList.end(); ) {
if(myCondition(*it)) {
std::list<MyClass>::iterator oldIt = it++;
myOtherList.splice(myOtherList.end(), myList, oldIt);
} else {
++it;
}
}
Solution 1
template<typename Fwd, typename Out, typename Operation>
Fwd move_if(Fwd first, Fwd last, Out result, Operation op)
{
Fwd swap_pos = first;
for( ; first != last; ++first ) {
if( !op(*first) ) *swap_pos++ = *first;
else *result++ = *first;
}
return swap_pos;
}
The idea is simple. What you want to do is remove elements from one container and place them in another if a predicate is true. So take the code of the std::remove() algorithm, which already does the remove part, and adapt it to your extra needs. In the code above I added the else line to copy the element when the predicate is true.
Notice that because we use the std::remove() code, the algorithm doesn't actually shrink the input container. It does return the updated end iterator of the input container though, so you can just use that and disregard the extra elements. Use the erase-remove idiom if you really want to shrink the input container.
Solution 2
template<typename Bidi, typename Out, typename Operation>
Bidi move_if(Bidi first, Bidi last, Out result, Operation op)
{
Bidi new_end = partition(first, last, not1(op));
copy(new_end, last, result);
return new_end;
}
The second approach uses the STL to implement the algorithm. I personally find it more readable than the first solution, but it has two drawbacks: First, it requires the more-powerful bidirectional iterators for the input container, rather than the forward iterators we used in the first solution. Second, and this is may or may not be an issue for you, the containers are not guaranteed to have the same ordering as before the call to std::partition(). If you wish to maintain the ordering, replace that call with a call to std::stable_partition(). std::stable_partition() might be slightly slower, but it has the same runtime complexity as std::partition().
Either Way: Calling the Function
list<int>::iterator p = move_if(l1.begin(), l1.end(),
back_inserter(l2),
bind2nd(less<int>(), 3));
Final Remarks
While writing the code I encountered a dilemma: what should the move_if() algorithm return? On the one hand the algorithm should return an iterator pointing to the new end position of the input container, so the caller can use the erase-remove idiom to shrink the container. But on the other hand the algorithm should return the position of the end of the result container, because otherwise it could be expensive for the caller to find it. In the first solution the result iterator points to this position when the algorithm ends, while in the second solution it is the iterator returned by std::copy() that points to this position. I could return a pair of iterators, but for the sake of making things simple I just return one of the iterators.
std::list<MyClass>::iterator endMatching =
partition(myList.begin(), myList.end(), myCondition);
myOtherList.splice(myOtherList.begin(), myList, endMatching, myList.end());
Note that partition() gives you enough to discriminate matching objects from non matching ones.
(list::splice() is cheap however)
See the following code on a concrete case inspired from
Now to remove elements that match a predicate?
#include <iostream>
#include <iterator>
#include <list>
#include <string>
#include <algorithm>
#include <functional>
using namespace std;
class CPred : public unary_function<string, bool>
{
public:
CPred(const string& arString)
:mString(arString)
{
}
bool operator()(const string& arString) const
{
return (arString.find(mString) == std::string::npos);
}
private:
string mString;
};
int main()
{
list<string> Strings;
Strings.push_back("213");
Strings.push_back("145");
Strings.push_back("ABC");
Strings.push_back("167");
Strings.push_back("DEF");
cout << "Original list" << endl;
copy(Strings.begin(), Strings.end(),ostream_iterator<string>(cout,"\n"));
CPred Pred("1");
// Linear. Exactly last - first applications of pred, and at most (last - first)/2 swaps.
list<string>::iterator end1 =
partition(Strings.begin(), Strings.end(), Pred);
list<string> NotMatching;
// This function is constant time.
NotMatching.splice(NotMatching.begin(),Strings, Strings.begin(), end1);
cout << "Elements matching with 1" << endl;
copy(Strings.begin(), Strings.end(), ostream_iterator<string>(cout,"\n"));
cout << "Elements not matching with 1" << endl;
copy(NotMatching.begin(), NotMatching.end(), ostream_iterator<string>(cout,"\n"));
return 0;
}
Another attempt:
for(std::list<MyClass>::iterator it = myList.begin(); it != myList.end; ) {
std::list<MyClass>::iterator eraseiter = it;
++it;
if(myCondition(*eraseiter)) {
myOtherList.push_back(*eraseiter);
myList.erase(eraseiter);
}
}
template <typename ForwardIterator, typename OutputIterator, typename Predicate>
void splice_if(ForwardIterator begin, ForwardIterator end, OutputIterator out, Predicate pred)
{
ForwardIterator it = begin;
while( it != end )
{
if( pred(*it) )
{
*begin++ = *out++ = *it;
}
++it;
}
return begin;
}
myList.erase(
splice_if( myList.begin(), myList.end(), back_inserter(myOutputList),
myCondition
),
myList.end()
)