Click to See Complete Forum and Search --> : implementation of FIND (part II)


Guysl
April 22nd, 2004, 05:42 PM
I came up with my version for FIND that was discussed in one of the previous posts.
I would like to get feedbacks, but first a few words:

a reminder about the original post:
say we have a vector of integers. by using syntax like:

vector<int> vec;
vec.push_back(...)
find(vec > 2 && vec < 5 || vec >12 && vec<14)

we would like to get all the values that between 2 to 5, or between 12 to 14.


I'm sure there are better ways to design and implement it since
I'm not familiar enough with templates nor with STL.
for now, I used only 2 operators, just for a demonstration,
and a container of vector, so here we go:

#include <vector>
#include <algorithm>
#include <functional>
#include <iostream.h>

using namespace std;


template<typename T>
class containrWrapper{

private:
struct result{
vector<T> vResults;

friend result operator &&(const result& lh, const result& rh)
{
result totalResult;
if( !lh.vResults.empty() && !rh.vResults.empty() )
{
result lhSorted = lh, rhSorted = rh ;
sort( lhSorted.vResults.begin(), lhSorted.vResults.end());
sort( rhSorted.vResults.begin(), rhSorted.vResults.end());

set_intersection( lhSorted.vResults.begin(), lhSorted.vResults.end(),
rhSorted.vResults.begin(), rhSorted.vResults.end(),
back_inserter(totalResult.vResults) );
}
return totalResult;
}

friend result operator ||(const result& lh, const result& rh)
{
result totalResult = lh;
totalResult.vResults.insert(totalResult.vResults.end(), rh.vResults.begin(), rh.vResults.end());
unique( totalResult.vResults.begin(), totalResult.vResults.end());
return totalResult;
}
};


public:

vector<T> vData;
containrWrapper(const vector<T>& vec) : vData(vec) {}

result operator>(const T& val)
{
result res;
remove_copy_if( vData.begin(), vData.end(), back_inserter(res.vResults),
bind2nd(less_equal<T>(), val) );
return res;
}


result operator<(const T& val)
{
result res;
remove_copy_if( vData.begin(), vData.end(), back_inserter(res.vResults),
bind2nd(greater_equal<T>(), val));
return res;
}

vector<T> Query(const result& query)
{
return query.vResults;
}
};

int main(int argc, char* argv[])
{

vector<int> Vect;
Vect.push_back(8);
Vect.push_back(10);
Vect.push_back(2);
Vect.push_back(22);
Vect.push_back(7);

containrWrapper<int> Wrapper(Vect);

vector<int> result = Wrapper.Query( Wrapper > 5 && Wrapper < 9 || Wrapper > 12);

for( int i=0; i < result.size(); i++)
cout << result[i] << " ";
return 0;
}

Gabriel Fleseriu
May 20th, 2004, 07:44 AM
I will let the (disputable) design goals be and list two points:
[list=1]
Use the new stream headers, that is <iostream> instead of <iostream.h>
Declaring friend functions for structs that don't have private/protected members doesn't make much sense
[/list=1]

As for the rest -- it works. I didn't look at every line of code, but you clearly show that it is possible to implement it.

Guysl
May 20th, 2004, 08:00 AM
I will let the (disputable) design goals be and list two points:



Use the new stream headers, that is <iostream> instead of <iostream.h>

Declaring friend functions for structs that don't have private/protected members doesn't make much sense




1. yeah, that code is quiet old, at that time I had bad installation of MSVC++,
as soon as I reinstalled it I could use the new stream headers.

2. the only sense I had, and maybe its MSVC++ related, is that
the struct is a nested private one. in order for non-member-function, as main(), to access these struct's operators -
but still with a restriction of declaring such a private struct -
I used the operators as friend (global).

Regards,
Guy

Yves M
May 20th, 2004, 08:19 AM
Wouldn't it make more sense to have a string that would serve as constraints and then build a simple parser?

In that case, you could do the following:

vector<int> a, b, res1, res2;
// Fill a and b
find_select(a.begin(), a.end(), "x > 5 && x < 9 || x > 12", back_insert_iterator<vector<int> >(res1));

find_select(b.begin(), b.end(), "x > 5 && x < 9 || x > 12", back_insert_iterator<vector<int> >(res2));

With this approach, you could re-use the code for vector, set, list etc.

edit:

Actually, thinking about this, it would probably suffice to define a predicate that accepts a string as condition (in the constructor), builds its parse tree and then in the function call operator returns whether the condition is fulfilled or not. Then you could use that with remove_copy_if and the like to achieve what I described above.

Guysl
May 20th, 2004, 09:05 AM
Yves,

I have some difficulties to express myself using english, but
I'll try to explain what was my way of thinking:

since expression are built-in language part, I was trying to
use the C++ language operators as a partial "parser".
no matter how you parse a string, you will get to a point where
you need to use operations with the parsing results.
The way I choose is something like an "interpreter" I believe,
that use the built-in operators and their precedence rules in order
to avoid string parsing.

another thing, I'm familiar only with a small part of STL,
otherwise I would have designed it differently.

Regards,
Guy

Yves M
May 20th, 2004, 09:21 AM
Ok fair enough, you avoid parsing the string, but you lose in flexibility and performance.

Gabriel Fleseriu
May 21st, 2004, 02:33 AM
Yves,

the original goal was to implement "something" so the "string-less" syntax was possible. It's a long story, and we all pointed out that there are better ways to design it. Guysl only showed that it is possible to implement something that behaves that way -- take it as a C++ exercise. He wasn't the starter of the original thread.

Yves M
May 21st, 2004, 12:57 PM
Ok, sorry, I hadn't read the other thread recently and forgot about that ;)

Guysl
May 21st, 2004, 01:11 PM
No probs at all.
I believe that as soon as I improve my skills with templates,
I'll redesign it for practice. I would like to try and compete
the speed of a parsed string and improve the flexibility issue.


Regards,
Guy