比较具有不同类型值的两个集合 [英] Comparing two sets with different type of values

查看:124
本文介绍了比较具有不同类型值的两个集合的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这是要求:



我有一个属性列表。每个属性都有一个int类型的标签,并且有以下类型之一的值:(bool,char,string,int,long)。
一些示例属性:


标记:1,值:某个字符串

标记:14 ,value:true

标签:20,值:123


还有一个规则列表。每个规则都像一个属性,加上下列运算符之一:(<>,< =,> =,==,!=,startsWith)。



一些示例规则:


$

b $ b


tag:1,value:hello,operator:==

tag:14,value:true,operator:==


每个规则列表称为 Criteria ,用于验证属性列表。如果只满足条件的所有规则,则条件验证属性列表。例如,上面的两个规则构成了一个标准。此标准不验证上述属性列表,因为第一个规则强制tag(1)的值等于(==)为hello,而不是。



以下条件:


标记:1,值: ,operator:==

tag:20,value:100,operator:>

tag:20,value:120,operator:<


也不会验证上述属性,因为虽然满足前两个规则,但最后一个规则强制tag(20)的值小于120不是真的。因此,整个标准不会验证属性。

这里是一个验证为true的条件:


:1,value:some,operator:startsWith

tag:14,value:false,operator:!=

tag:20,value:123,operator:

tag:20,value:100,operator:>


到目前为止,码。但我被困在执行 StartsWith 运算符。也许我应该选择一个完全不同的方法来解决这个问题。任何建议和帮助将非常感谢!

  #include< string> 
#include< iostream>
#include< set>
#include< algorithm>
#include< tr1 / memory>

using namespace std;

///////////////////物业类/////////////////////// /

class Property
{
public:
enum Operator
{
EQ,// ==
NEQ, /!=
GT,//>
GTE,//> =
LT,//<
LTE,//< =
SW,//以
开头} mOperator;

Property(){};
属性(const int tag,Operator op)
:mTag(tag),mOperator(op){}
virtual〜Property(){}
virtual void * GetValue = 0;
virtual bool IsEqual(void * value)= 0;
virtual bool比较(void * value,Property :: Operator op)= 0;
virtual void Print()= 0;
int mTag;
bool operator<(const Property& property)const
{
return mTag< property.mTag;
}
};
struct PropertyPtrComp
{
bool operator()(const std :: tr1 :: shared_ptr< Property> lhs,const std :: tr1 :: shared_ptr< Property> rhs)const
{
return lhs-> mTag< rhs→mTag;
}
};

/////////////////// TypedProperty Class /////////////////

template<类型名T>
class TypedProperty:public Property
{
public:
TypedProperty(const int tag,const T& value,Property :: Operator op)
: opal),mValue(value){}
void * GetValue()
{
return& mValue;
}
bool IsEqual(void * value)
{
return *((T *)value)== mValue;
}
bool比较(void * value,Property :: Operator op)
{
// cout< 比较<< *((T *)值) with<< mValue<< endl;
switch(op)
{
case Property :: EQ:
return *((T *)value)== mValue;
case Property :: NEQ:
return *((T *)value)!= mValue;
case属性:: GT:
return mValue> *((T *)值);
case Property :: LT:
return mValue< *((T *)值);
case Property :: SW:
{
if(typeid((T *)value)== typeid(string *))
{
//不知道该怎么办!
// return((string)mValue).compare(0,(string *)value) - > length(),(string)
}
}
默认值:
return *((T *)value)== mValue;
}
}
void Print()
{
cout< Tag:< mTag < ,值:< mValue<< endl;
}
T mValue;
};

////////////////////////////////////////// ///////////////

typedef std :: tr1 :: shared_ptr< Property> PropertyPtr;

/////////////////// PropertyList类/////////////////

class PropertyList
{
public:
PropertyList(){};
virtual〜PropertyList(){};
template< class T>
void Add(int tag,T value,Property :: Operator op = Property :: EQ)
{
PropertyPtr ptr(new TypedProperty< T&
mProperties.insert(ptr);
}
void Print()
{
cout< -----------<< endl;
for(set< PropertyPtr> :: iterator itr = mProperties.begin(); itr!= mProperties.end(); itr ++)
{
(* itr) - > );
}
}
set< PropertyPtr,PropertyPtrComp>性能;
};

////////////////////检查子集////////////////////// ///
/ *
*检查子集中是否包含子集
* /
bool CheckSubset(set< PropertyPtr,PropertyPtrComp>& superset,
set< Property(); propertyPtrComp>& subset)
{
if(subset.size()> superset.size()|| subset.empty())return false;
typename set< PropertyPtr> :: iterator litr = superset.begin();
for(typename set< PropertyPtr> :: iterator ritr = subset.begin(); ritr!= subset.end();)
{
while(litr!= superset.end ))
{
PropertyPtr lProp =(PropertyPtr)* litr;
PropertyPtr rProp =(PropertyPtr)* ritr;
if(lProp-> mTag == rProp-> mTag)
{
if(lProp-> Compare(rProp-> GetValue(),rProp-> mOperator) )
{
litr ++;
break;
}
return false;
}
else
{
litr ++;
}
}
ritr ++;
if(litr == superset.end()&& ritr!= subset.end())return false;
}
return true;
}

int main()
{
PropertyList属性;

string s =bye;

properties.Add(1,hello);
properties.Add(2,12);
properties.Add(3,34);
properties.Add(4,bye);

properties.Print();

PropertyList ruleSet1;
ruleSet1.Add(2,12,Property :: EQ);
ruleSet1.Add(4,bye,Property :: EQ);

ruleSet1.Print();

if(CheckSubset(properties.mProperties,ruleSet1.mProperties))
cout<< RuleSet1 verified! << endl; //<<<<应该打印
else
cout<< RuleSet1未验证! << endl;


PropertyList ruleSet2;
string hel =hel;
ruleSet2.Add(1,hel,Property :: SW);
ruleSet2.Add(2,13,Property :: NEQ);

ruleSet2.Print();

if(CheckSubset(properties.mProperties,ruleSet2.mProperties))
cout<< RuleSet2 verified! << endl; //<<<<应该打印
else
cout<< RuleSet2未验证! << endl;


}


解决方案

不要使用void *,请尝试boost :: variant。构建对每个操作符进行操作的测试函子。您比较的值是另一个变体。你可能需要函子的函子,所以你可以做任何转换你想做的两个变体之间。



这种类型和存储从每种类型的算法分离。



你的测试然后成为一对标记和 std :: function< bool



一个例子,只有两种类型。

p>

从int到变体开始: typedef std :: map< int,boost :: variant< int,string> MyMap



现在进行测试。一个测试是3个东西 ​​- 一个id,一个值(它是一个变体)和一个测试来比较该变体与数据。如果我们不允许字符串和整数相等,我们得到:

  template< typename T& 
struct CheckOneTypeEquality
{
T const * value;
bool operator()(T const& other)const
{
return other == * value;
}
template< typename U>
bool operator()(U const& other)const
{
return false;
}
CheckOneTypeEquality(T const& value_):value(& value_){}
};

template< typename Variant>
struct TestVariantEquality
{
Variant * v;
struct FindRightToCompare
{
Variant * other;
template< typename T>
bool operator()(T const& value)const
{
return apply_visitor(* other,CheckOneTypeEquality< T&
}
FindRightToCompare(Variant const& other_):other(other_){}
};

bool operator()(Variant const& other)const
{
return apply_visitor(* v,FindRightToCompare(other));
}
};

...执行编译时检查的等式比较。以上所有在左和右操作数上都有双 apply_visitor ,并且 CheckOneTypeEquality 具有 T 匹配左操作数的类型,右操作数作为 U 传递到 operator()



在这种情况下,我们只支持相同类型的相等 - 如果我们想允许 int long 进行比较,我们可以恰当地专门化 CheckOneTypeEquality< T> :: operator ; U>



我们还可以通过将 CheckOneTypeEquality 一个非模板类型,并将它传递到 TestVariantEquality 类,除了安排我们正在比较的两种类型的double-dispatch。这意味着 TestVariantEquality (更好的是,更通用的版本,它接受最终的比较函数作为模板参数)可以写一次,你只需要写a CheckOneTypeEquality 每个比较测试的等效函数。



免责声明:以上代码尚未由我编译,基于快速阅读在线文档。在上面的代码中没有使用命名空间,因为我很懒,但一切都基本上是从 std boost boost



请注意 TestVariantEquality 或类似的函子)可以用于初始化 std :: function< bool(Variant< ...> const&)> 。因此,生成测试函数的函数应该返回 std :: function< bool(Variant< ...> const&)>

$ d
$ b

属性应该是 std :: map< int,boost: :variant< blah blah>> 。您的测试应为 std :: multimap< int,std :: function< bool(boost :: variant< blah blah> const&)>> p>

将要测试的数据加载到属性中。使用类似上面的技术来构建测试。不需要虚拟函数,没有void指针,一切都是编译时类型检查。


Here is the requirement:

I've got a list of properties. Each property has a tag of type int and a value of one of the following types: (bool, char, string, int, long). some sample properties:

tag: 1, value: "some string"
tag: 14, value: true
tag: 20, value: 123

There is also a list of rules. Each rule is like a property, plus one of the following operators: (<, >, <=, >=, ==, !=, startsWith). The startsWith operator is used to check whether one string starts with another one.

Some sample rules:

tag: 1, value: "hello", operator: ==
tag: 14, value: true, operator: ==

Each list of rules is called a Criteria and is used to validate a list of properties. A criteria validates a list of properties if only all the rules of the criteria are met. For example, the two rules above make up a criteria. This criteria does not validate the aforementioned list of properties because the 1st rule forces the value of tag (1) to be equal (==) to "hello" which is not.

The following criteria:

tag: 1, value: "some string", operator: ==
tag: 20, value: 100, operator: >
tag: 20, value: 120, operator: <

does not validate the above properties too, because although the first two rules are met, the last rule, which forces the value of tag (20) to be less than 120 is not true. Therefore the whole criteria does not validate the properties.
And here is a criteria that validates to true:

tag: 1, value: "some", operator: startsWith
tag: 14, value: false, operator: !=
tag: 20, value: 123, operator: >=
tag: 20, value: 100, operator: >

So far I have come up with the following code. But I am stuck in the implementation of StartsWith operator. Maybe I should of chosen a totally different approach to solve this problem. Any advice and help would be greatly appreciated!

#include <string>
#include <iostream>
#include <set>
#include <algorithm>
#include <tr1/memory>

using namespace std;

/////////////////// Property Class //////////////////////

class Property
{
public:
    enum Operator
    {
        EQ,     // ==
        NEQ,    // !=
        GT,     // >
        GTE,    // >=
        LT,     // <
        LTE,    // <=
        SW,     // Starts With
    } mOperator;

    Property() {};
    Property(const int tag, Operator op)
            : mTag(tag), mOperator(op) {}
    virtual ~Property() {}
    virtual void* GetValue() = 0;
    virtual bool IsEqual(void* value) = 0;
    virtual bool Compare(void* value, Property::Operator op) = 0;
    virtual void Print() = 0;
    int mTag;
    bool operator<(const Property &property) const
    {
        return mTag < property.mTag;
    }
};
struct PropertyPtrComp
{
    bool operator()(const std::tr1::shared_ptr<Property> lhs, const std::tr1::shared_ptr<Property> rhs) const
    {
        return lhs->mTag < rhs->mTag;
    }
};

/////////////////// TypedProperty Class /////////////////

template< typename T >
class TypedProperty : public Property
{
public:
    TypedProperty (const int tag, const T& value, Property::Operator op)
            : Property(tag, op), mValue(value) {}
    void* GetValue()
    {
        return &mValue;
    }
    bool IsEqual(void* value)
    {
        return *((T*)value) == mValue;
    }
    bool Compare(void* value, Property::Operator op)
    {
        // cout << "comparing " << *((T*)value) << " with " << mValue << endl;
        switch (op)
        {
           case Property::EQ:
               return *((T*)value) == mValue;
           case Property::NEQ:
               return *((T*)value) != mValue;
           case Property::GT:
               return mValue > *((T*)value) ;
           case Property::LT:
               return mValue < *((T*)value) ;
           case Property::SW:
           {
               if (typeid((T*)value) == typeid(string*))
               {
               // dont know what to do!
               //return ((string)mValue).compare(0, ((string*)value)->length(), (string)(*((string*)value)));
               }
           }
           default:
               return *((T*)value) == mValue;
        }
    }
    void Print()
    {
        cout << "Tag: " <<  mTag << ", Value: " << mValue << endl;
    }
    T mValue;
};

/////////////////////////////////////////////////////////

typedef std::tr1::shared_ptr<Property> PropertyPtr;

/////////////////// PropertyList Class /////////////////

class PropertyList
{
public:
    PropertyList() {};
    virtual ~PropertyList() {};
    template <class T>
    void Add(int tag, T value, Property::Operator op = Property::EQ)
    {
        PropertyPtr ptr(new TypedProperty<T>(tag, value, op));
        mProperties.insert(ptr);
    }
    void Print()
    {
        cout << "-----------" << endl;
        for (set<PropertyPtr>::iterator itr = mProperties.begin(); itr != mProperties.end(); itr++)
        {
            (*itr)->Print();
        }
    }
    set<PropertyPtr, PropertyPtrComp> mProperties;
};

//////////////////// Check Subset ///////////////////////
/*
 * Checks if subset is included in superset
 */
bool CheckSubset(set<PropertyPtr, PropertyPtrComp> &superset, 
         set<PropertyPtr, PropertyPtrComp> &subset)
{
    if (subset.size() > superset.size() || subset.empty()) return false;
    typename set<PropertyPtr>::iterator litr = superset.begin();
    for (typename set<PropertyPtr>::iterator ritr = subset.begin(); ritr != subset.end();)
    {
        while (litr != superset.end())
        {
            PropertyPtr lProp = (PropertyPtr)*litr;
            PropertyPtr rProp = (PropertyPtr)*ritr;
            if (lProp->mTag == rProp->mTag)
            {
                if (lProp->Compare(rProp->GetValue(), rProp->mOperator))
                {
                    litr++;
                    break;
                }
                return false;
            }
            else
            {
                litr++;
            }
        }
        ritr++;
        if (litr == superset.end() && ritr != subset.end()) return false;
    }
    return true;
}

int main()
{
    PropertyList properties;

    string s = "bye";

    properties.Add(1, "hello");
    properties.Add(2, 12);
    properties.Add(3, 34);
    properties.Add(4, "bye");

    properties.Print();

    PropertyList ruleSet1;
    ruleSet1.Add(2, 12, Property::EQ);
    ruleSet1.Add(4, "bye", Property::EQ);

    ruleSet1.Print();

    if(CheckSubset(properties.mProperties, ruleSet1.mProperties)) 
    cout << "RuleSet1 verified!" << endl;   // <<<< should be printed
    else
        cout << "RuleSet1 NOT verified!" << endl; 


    PropertyList ruleSet2;
    string hel = "hel";
    ruleSet2.Add(1, hel, Property::SW);
    ruleSet2.Add(2, 13, Property::NEQ);

    ruleSet2.Print();

    if (CheckSubset(properties.mProperties, ruleSet2.mProperties)) 
        cout << "RuleSet2 verified!" << endl;   // <<<< should be printed
    else
        cout << "RuleSet2 NOT verified!" << endl;


}

解决方案

Do not use void*, try boost::variant. Build test functors that operate on each and every operator. The value you compare to is another variant. You may need functors of functors so you can do whatever conversion you want to do between two variants.

This separates type and storage from algorithms on each type. And boost does variant types better than you do.

Your tests then become a pair of tag and std::function<bool(boost::variant<int,long,string,etc>)> which does the test.

An example, with only two types.

Start with a map from int to variant: typedef std::map<int, boost::variant<int, string>> MyMap.

Now a test. A test is 3 things -- an id, a value (which is a variant) and a test to compare that variant to the data. If we don't allow strings and ints to be equal, we get:

template<typename T>
struct CheckOneTypeEquality
{
  T const* value;
  bool operator()( T const& other ) const
  {
    return other == *value;
  }
  template<typename U>
  bool operator()( U const& other ) const
  {
    return false;
  }
  CheckOneTypeEquality( T const& value_ ):value(&value_) {}
};

template<typename Variant>
struct TestVariantEquality
{
  Variant* v;
  struct FindRightToCompare
  {
    Variant* other;
    template<typename T>
    bool operator()( T const& value ) const
    {
      return apply_visitor( *other, CheckOneTypeEquality<T>(value) );
    }
    FindRightToCompare( Variant const& other_ ): other(other_) {}
  };

  bool operator()( Variant const& other ) const
  {
    return apply_visitor( *v, FindRightToCompare(other) );
  }
};

... which does a compile-time checked equality comparison. All the above does a double apply_visitor on both the left and right operands, and CheckOneTypeEquality<T> has the T match the type of the left operand, and the right operand is passed in as a U to the operator().

In this case, we only support equality of the same type -- if we wanted to allow int and long to compare, we could just appropriately specialize the CheckOneTypeEquality<T>::operator()<U>.

We can also abstract the above by wrapping the CheckOneTypeEquality in a non-template type and passing it into the TestVariantEquality class, which does nothing except arrange for the double-dispatch of the two types we are comparing. Which means that the TestVariantEquality (well, the more generic version, which takes the final comparison functor as a template parameter) could be written once, and you'd just have to write a CheckOneTypeEquality equivalent functor for each comparison test.

Disclaimer: Above code has not been compiled by me, and is based off a quick reading of online docs. Namespaces are not used in the above code because I was lazy, but everything is basically from std or boost or a sub-namespace of boost.

Note that TestVariantEquality (or similar functors) can be used to initialize a std::function<bool(Variant<...> const&)>. So the function that produces the test functions should return std::function<bool(Variant<...> const&)>, and internally can do the above type of stuff.

properties should be a std::map<int, boost::variant<blah blah>>. Your tests should be a std::multimap<int, std::function<bool(boost::variant<blah blah> const&)>>.

Load the data you are testing into the properties. Build the tests using something like the above technique. No virtual functions required, no void pointers, and everything is compile-time type checked.

这篇关于比较具有不同类型值的两个集合的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆