用于将对象属性与规则匹配的算法

时间:2013-05-18 07:29:11

标签: c# algorithm haskell

道歉标题不清楚。这是解释:

我有一种具有属性a,b,c,d的对象Foo。让我们说这些属性(string / bool的类型)可以分别具有3个唯一值(a有1,2,3; b有11,12,13等等)。

我有一套规则,我希望与Foo对象列表匹配。规则可以具有一个或多个具有选定值的属性。例: 规则1:a = 1 规则2:b = 12和a = 2

我想知道什么是获得规则匹配的最佳方式(基于C#/ Haskell的解决方案会更好,尽管对算法的解释也很好)。

我提到C#,因为如果有任何可能的方法我们可以使用LINQ进行这些匹配,我会感兴趣。 Haskell被提及作为函数式语言的代理,因此是一种递归的,无分支的方法。

我目前正在使用字典来构建规则,然后使用反射来完成匹配。我最喜欢当前的解决方案是,如果我们需要添加一个新属性,那么它很容易,而且由于分支较少,代码很容易理解。

添加示例以提高清晰度

我们有一个具有以下属性的动物对象列表

Object:Animal
Properties: Color, LivingEnvironment, Place, Mammal (all properties are of type string)

数据:

Animal1 : Red, Water, Arctic, No
Animal2 : Black, Land, Asia, No
Animal3 : Blue, Land, UK, Yes

规则

Rule1 : Color=Red And LivingEnvironment=Land
Rule2 : Color=Red And LivingEnvironment=Water
Rule3 : COlor=Blue And Place=UK And Mammal=Yes

规则可以从用户界面配置,因此在编译时不知道它们。 用户可能会将规则3更改为新定义

Rule3 : Color=Blue And PLace=UK

我希望这可以澄清之前造成的一些混乱。

4 个答案:

答案 0 :(得分:2)

规则只是一个功能:

type Rule = Foo -> Bool

这是制定规则的功能:

(=:=) :: Eq a => (Foo -> a) -> a -> Rule
f =:= x = \foo -> f foo == x

(例如a =:= 1

以下是一些组合规则的功能:

allRules, anyRules :: [Rule] -> Rule
allRules rules foo = all ($ foo) rules
anyRule  rules foo = any ($ foo) rules

(例如allRules [b =:= 12, a =:= 2]

使用标准filter功能过滤您的[Foo]


您想要从配置文件中读取规则。我假设您从读取/解析配置中获得了一对字符串列表。

让我们从一个函数开始,将一对字符串转换为规则:

readRule :: String -> String -> Maybe Rule
readRule = fieldName requiredValue = do
    constructRule <- lookup fieldName ruleDefs
    constructRule requiredValue

ruleDefs :: [(String, String -> Maybe Rule)] -- should be a Map irl

现在让我们编写一个辅助函数来生成ruleDefs中的条目:

ruleEntry :: (Read a, Eq a) => String -> (Foo -> a) -> String -> Maybe Rule
ruleEntry name project = (name, constructRule) where
    constructRule requiredValue
        = case filter (null . snd) (reads requiredValue) of
            [(value, _)] -> Just (value ==)
            _            -> Nothing

除了帮助函数之外,你可以手写ruleDefs

ruleDefs = [
    ruleEntry "alpha" alpha,
    ruleEntry "beta"  beta,
    ruleEntry "gamma" gamma,
    ruleEntry "delta" delta]

此构造适用于字段(例如alpha中的betadata Foo = Foo { alpha :: Int, beta :: Int })和计算字段(例如delta foo = alpha foo - beta foo)。我将展示一些用于构建ruleDefs的技术,而不需要重复键入,并且它们都将使用模板Haskell。

(更多信息。)

答案 1 :(得分:1)

如果您只有一条规则:

您所谓的规则很简单predicatesspecifications。我将根据一些规则向您展示两种在C#中过滤Foo个对象集合的方法。对于这两个示例,我们假设我们有一个Foo[] foos

使用LINQ的C#示例:

Func<Foo, bool>委托类型适用于Foo个对象的谓词函数:

Func<Foo, bool>  someRule     = foo => foo.a == 2 && foo.b == 12;
IEnumerable<Foo> matchingFoos = foos.Where(someRule);

没有LINQ的C#示例:

在有各种Func<>委托类型之前,.NET类库已经有Predicate<T>,这是合适的:

Predicate<Foo> someRule     = delegate(Foo foo) { return foo.a == 2 && foo.b == 12; };
Foo[]          matchingFoos = Array.FindAll(foos, someRule);

(注意,与基于LINQ的解决方案不同,这个解决方案返回一个集合,而不是一个延迟评估的序列。此外,匿名委托与lambda语法的选择独立于LINQ,但我选择了较旧的语法非LINQ示例,因为那是当时C#语言在版本3和LINQ引入之前的样子。)

当你有几条规则时:

然后,在将Foo个对象与之匹配之前,必须以某种方式将它们组合在一起。也就是说,您需要确定Foo是否必须匹配所有规则(逻辑AND)或至少一个(逻辑OR)等。您可以从两个给定的规则中获取组合规则,如下所示:

static Func<Foo, bool> And(this Func<Foo, bool> ruleA, Func<Foo, bool> ruleB)
{
    return x => ruleA(x) && ruleB(x);
}

static Func<Foo, bool> Or(this Func<Foo, bool> ruleA, Func<Foo, bool> ruleB)
{
    return x => ruleA(x) || ruleB(x);
}

Func<Foo, bool> ruleA        = foo => foo.a == 2;
Func<Foo, bool> ruleB        = foo => foo.b == 12;
Func<Foo, bool> combinedRule = ruleA.And(ruleB);

由于您允许用户定义规则,您可能不希望在这些规则中硬连接常量;所以你可以创建工厂方法(或类),例如:

Func<Foo, bool> PropertyAEquals(int value)
{
    return foo => foo.a == value;
}

Func<Foo, bool> PropertyBEquals(int value)
{
    return foo => foo.b == value;
}

您可以根据需要灵活调整。您只需要返回Func<Foo, bool>的工厂方法或类,以及将用户输入从UI转换为对正确工厂方法的调用所需的逻辑。

答案 2 :(得分:0)

 public bool AbidingByRule(Dictionary<string,object> rule)
         {
             var type=this.GetType();
             int unmatchedCount=rule.Count(r => !r.Value.Equals(type.GetProperty(r.Key).GetValue(this, null)));
             return unmatchedCount == 0;
         }

答案 3 :(得分:0)

您可以使用NCalclink)将简单表达式解析为规则,例如:

    class Foo
    {
        public int a { get; set; }
        public int b { get; set; }
        public int c { get; set; }
    }

    static bool VerifyRule(Foo obj, string rule)
    {
        NCalc.Expression expr = new NCalc.Expression(rule);
        expr.EvaluateParameter += (name, args) =>
        {
            args.Result = typeof(Foo).GetProperty(name).GetValue(obj, null);
        };
        return (bool)expr.Evaluate();
    }

    // USAGE EXAMPLE
    static void Main(string[] args)
    {
        var foo1 = new Foo() { a = 3, b = 4, c = 12 };
        var foo2 = new Foo() { a = 1, b = 4, c = 12 };

        // verify rules
        var res1 = VerifyRule(foo1, "a == 3 && b == 4"); // returns true
        var res2 = VerifyRule(foo2, "a == 3 && b == 4"); // returns false

        // more complex rules:
        var res3 = VerifyRule(foo1, "(a < 4 && b > 5) || c == 12"); // returns true
        var res4 = VerifyRule(foo1, "a + b == 7"); // returns true
    }

注意
我还在这里使用反射。在我看来,你无法避免这种情况,因为你的规则是通过UI动态定义的......