我需要根据一组相当大的8个相关依赖条件做出决定。
| A | B | C | D | E | F | G | H
-----------+---+---+---+---+---+---+---+---
Decision01 | 0 | 1 | - | 1 | 0 | 1 | - | 1
Decision02 | 1 | 0 | - | 0 | 0 | - | 1 | -
...
Decision11 | 1 | 0 | 1 | 1 | 1 | - | 1 | 1
从A到H的每个条件可以是真(1),假(0)或不相关( - )用于决定。
所以使用
的给定输入A B C D E F G H
1 0 1 0 0 1 1 1
它应该评估为Decision02。
决策是明确的,因此从任何给定的输入条件集中,必须明确哪个决策(并且在决策矩阵未涵盖的情况下,应抛出异常)。
在这个项目之前在我之前工作过的开发人员试图将其作为一个500行长嵌套来实现 - 如果这个庞然大物当然是错误的,并且不可维护。
所以我搜索了实现这样一个逻辑的最佳方法,我发现了决策表/查找表/控制表。
我发现了很多决策表生成器,但没有关于如何实现决策过程的单一代码:(
我可以在底层MSSQL数据库中创建决策表,或者在代码或xml中创建决策表,或者它可以做任何事情。我只需要一些关于如何实现这一点的指示。
实施此逻辑的最佳做法是什么?字典?多维数组?完全不同的东西?
答案 0 :(得分:6)
你可以使用Func数组。
static Func<bool,bool> isTrue = delegate(bool b) { return b; };
static Func<bool,bool> isFalse = delegate(bool b) { return !b; };
static Func<bool,bool> isIrrelevant = delegate(bool b) { return true; };
现在你可以把你的矩阵放到这样的字典中:
Dictionary<string,Func<bool,bool>[]> decisionMatrix = new Dictionary<string,Func<bool,bool>[]>();
// 0 | 1 | - | 1 | 0 | 1 | - | 1
matrix.Add("Decision01", new Func<bool,bool>{isFalse, isTrue, isIrrelevant, isTrue, isFalse, isTrue, isIrrelevant, isTrue});
最后为每个给定的输入数组:
bool[] input = new bool[]{ false, true, false, true, false, true, false, true}
string matchingRule = null;
foreach( var pair in matrix ) {
bool result = true;
for( int i = 0; i < input.Length; i++) {
// walk over the function array and call each function with the input value
result &= pair.Value[i](input[i]);
}
if (result) { // all functions returned true
// we got a winner
matchingRule = pair.Key;
break;
}
}
// matchingRule should now be "Decision01"
这可能会得到更多的检查(例如检查输入数组的大小是否正确),但应该给你一些想法。如果你得到第四个状态,使用Funcs还可以提供更多的灵活性。
答案 1 :(得分:2)
我会使用Dictionary<TKey, TValue>
的2D数组(在我们的例子中是bool?
) - 请注意? for Nullable<bool>
允许3种状态:true,false和null。您的null可能代表“无效”......
定义数组:
var myArray = new Dictionary<char, Dictionary<int, bool?>>();
然后你可以做以下事情:
bool result = false;
foreach (var inputPair in input)
{
// Assuming inputPair is KeyValuePair<char, int>
result |= myArray[inputPair.Key][inputPair.Value];
}
return result;
答案 2 :(得分:2)
这就是我喜欢LINQ的方式。
首先,您的矩阵是IEnumerable<IEnumerable<bool?>>
,true
表示1,false
,0和null
不确定。
然后传递要检查的IEnumerable<bool>
。这是功能:
public IEnumerable<bool?> DecisionMatrix(this IEnumerable<bool> source, IEnumerable<IEnumerable<bool?>> options)
{
IList<bool> sourceList = source.ToList();
return options.Where(n => n.Count() == sourceList.Count)
.Select(n => n.Select((x, i) => new {Value = x, Index = i}))
.Where(x =>
x.All(n => !(sourceList[n.Index] ^ n.Value ?? sourceList[n.Index])))
.FirstOrDefault();
}
(这是一种扩展方法,把它放在static class
:))
答案 3 :(得分:1)
您可以使用两个字节字段表示决策类。第一个字节将指定哪些条件为真或假。第二个字节将指定哪些条件是相关的。此外,您可以定义一个函数来确定输入字节是否与对象匹配。
由此,您可以创建一个包含决策列表的矩阵类,然后使用LINQ在列表中搜索与您的输入匹配的决策。
你可以拥有像这样的决策课
class Decision
{
byte Conditions;
byte RelevantConditions;
bool IsMatch(byte input)
{
byte unmatchedBits = input ^ Conditions; //matching conditions are set to 0
unmatchedBits &= RelevantConditions; //Irrelevant conditions set to 0
return (unmatchedBits == 0); //if any bit is 1, then the input does not match the relevant conditions
}
}
因此,Decision01的对象可以定义为
Decision decision01 = new Decision()
{
Conditions = 0x55; //01010101 in binary
RelevantConditions = 0xdd; //11011101 in binary
}
然后您的Decision Matrix类可以像这样制作
class DecisionMatrix
{
List<Decision> decisions;
Decision Find(byte input)
{
return decisions.Find(d => d.IsMatch(input));
}
}
创建一个包装字节的Input类也可能有所帮助。使用A-H字段实例化Input对象时,会创建一个字节以匹配这些字段。
答案 4 :(得分:1)
您可以将决策矩阵实现为字典,如下所示,并在矩阵上查询以查找匹配项。我已经使用string.join将数组转换为字符串。也使用矩阵中的' - '作为正则表达式[0 | 1]。
Dictionary<string, char[]> myMatrix = new Dictionary<string, char[]>();
myMatrix.Add("Decision01", new char[] { '0', '1', '-', '1', '0', '1', '-', '1' });
myMatrix.Add("Decision02", new char[] { '1', '0', '-', '0', '0', '-', '1', '-' });
myMatrix.Add("Decision03", new char[] { '1', '1', '1', '0', '0', '1', '1', '1' });
char[] input = new char[] { '1', '0', '1', '0', '0', '1', '1', '1' };
var decision = (from match in myMatrix
where Regex.IsMatch(string.Join(string.Empty, input),
string.Join(string.Empty, match.Value).ToString().Replace("-", "[0|1]"),
RegexOptions.IgnoreCase)
select match.Key).FirstOrDefault();
Console.WriteLine(decision);
答案 5 :(得分:0)
您可以在几行中创建并创建二进制计算器。因此,在下面的示例中,结果= 182而不是决策D(或每个)。下面的内容与您的决定和结果排列在一起。
这是一个关于二进制的网站[http://electronicsclub.info/counting.htm]谢谢google。
例如二进制的10110110等于十进制的182:
数字值:128 64 32 16 8 4 2 1
二进制数:1 0 1 1 0 1 1 0
十进制值:128 + 0 + 32 + 16 + 0 + 4 + 2 + 0 = 182