2013-06-03 16 views
16

Ho bisogno di prendere una decisione sulla base di un insieme piuttosto ampio di 8 condizioni dipendenti.Come implementare la matrice delle decisioni in C#

  | 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 

Ognuna delle condizioni da A ad H può essere vero (1), false (0) o non rilevante (-) per la decisione.

Quindi, con una data di ingresso di

A B C D E F G H 
1 0 1 0 0 1 1 1 

si dovrebbe valutare a Decision02.

Le decisioni sono inequivocabili, quindi da una serie di condizioni di input è chiaro quale decisione deve essere presa (e in un caso che non è coperto dalla matrice decisionale, verrà lanciata un'eccezione).

Lo sviluppatore che ha lavorato prima di me in questo progetto ha cercato di implementarlo come un behemoth nidificato da 500 righe, che ovviamente è bacato e non è gestibile.

Quindi ho cercato il modo migliore per implementare una tale logica e mi sono imbattuto in tabelle decisionali/tabelle di consultazione/tabelle di controllo.

Ho trovato un sacco di generatori di tabella di decisione, ma non un singolo pezzo di codice su come attuare il processo decisionale :(

posso fare la tabella di decisione nel database MSSQL sottostante, o in codice, o XML, o qualsiasi cosa. ho solo bisogno di alcune indicazioni su come implementare questo a tutti.

Qual è la migliore pratica per implementare questa logica? dizionario? Array multidimensionale? Qualcosa di completamente diverso?

+0

Nullable booleano è dove vorrei iniziare ... bool? Può essere true false o null – Sayse

+0

@Sayse By - indica che non è importante, ad esempio può essere 1 o 0. –

risposta

6

si potrebbe fare con array di Funz.

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; }; 

Ora si potrebbe mettere la vostra matrice in un dizionario come questo:

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}); 

Infine per ogni dato array di input:

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" 

Questo dovrebbe probabilmente ottenere alcuni più controlli (ad esempio, il controllo che l'array di input ha le dimensioni corrette), ma dovrebbe darti un'idea. L'uso di Func ti offre anche una maggiore flessibilità nel caso in cui si ottenga un quarto stato.

+1

Hai chiamato sia il tuo 'stringa' che il tuo risultato' bool' " – jszigeti

+1

Grazie, l'ho risolto.E dovrebbe essere Func invece di Func , risolto anche quello. –

+0

Funziona come un fascino! –

2

I utilizzare un array 2D (Dictionary<TKey, TValue> nel nostro caso) di bool? - nota il ? per Nullable<bool> che consente 3 stati: true, false e null. Il tuo nulla potrebbe rappresentare "nessun effetto" ... serie

Definito:

var myArray = new Dictionary<char, Dictionary<int, bool?>>(); 

allora si potrebbe fare cose come:

bool result = false; 
foreach (var inputPair in input) 
{ 
    // Assuming inputPair is KeyValuePair<char, int> 
    result |= myArray[inputPair.Key][inputPair.Value]; 
} 

return result; 
+1

Avviare il bool con false e fare '&' con il falso ottieni sempre false ... – Aristos

+0

You ' Ho ragione, era solo un esempio di cosa si poteva fare, non COSA fare testualmente. Aggiungerò il codice allo stesso modo in un OR invece – Haney

+1

Ok, ora capisci che sul primo vero, è vero per il resto del ciclo, e tu sei quello che hai vinto -default false, uno vero tutto vero- non. Quindi, il primo vero, restituire semplicemente vero, non continuare. – Aristos

0

Si potrebbe fare in un paio di linee e creare un calcolatore binario. Quindi, in un esempio qui sotto, i risultati = 182 rispetto alla decisione D (o cosa ogni). Il sotto, allinea con le tue decisioni e i risultati saranno tutti diversi totali.

Questo è un sito Web che utilizza il binario [http://electronicsclub.info/counting.htm] grazie a Google.

Ad esempio 10.110.110 in binario uguale 182 in decimale: Valore cifra: 128 64 32 16 8 4 2 1
numero binario: 1 0 1 1 0 1 1 0
valore decimale: 128 + 0 + 32 + 16 + 0 + 4 + 2 + 0 = 182

1

È possibile avere una classe di decisione rappresentata con due campi di byte. Il primo byte indicherà quali condizioni sono vere o false. Il secondo byte indicherà quali condizioni sono rilevanti. Inoltre, è possibile definire una funzione che determina se un byte di input corrisponde a un oggetto.

Da questo, è possibile creare una classe matrice che racchiude un elenco di decisioni, quindi utilizza LINQ per cercare nell'elenco una decisione che corrisponda al proprio input.

si può avere voi classe decisione come questa

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 
    } 
} 

Quindi, l'oggetto per Decision01 può essere definito come

Decision decision01 = new Decision() 
{ 
    Conditions   = 0x55; //01010101 in binary 
    RelevantConditions = 0xdd; //11011101 in binary 
} 

Poi la classe decisione Matrix può essere fatto in questo modo

class DecisionMatrix 
{ 
    List<Decision> decisions; 

    Decision Find(byte input) 
    { 
     return decisions.Find(d => d.IsMatch(input)); 
    } 
} 

Può anche aiutare a creare una classe di input che avvolge un byte. Quando si crea un'istanza di un oggetto di input con i campi A-H, viene creato un byte per abbinare questi campi.

2

Questo è come lo farei, con il mio amore per LINQ.

In primo luogo, le vostre matrici sono un IEnumerable<IEnumerable<bool?>>, e true significa 1, false, 0 e null indeterminato.

Quindi si passa un IEnumerable<bool> che si desidera controllare. Ecco la funzione:

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(); 
} 

(Si tratta di un metodo di estensione, metterlo in un static class :))

1

è possibile implementare la matrice decisione come un dizionario come mostrato di seguito e interrogare sulla matrice per trovare una corrispondenza . Ho usato string.join per convertire l'array in una stringa. Ho anche usato il '-' nella matrice come regex [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); 
Problemi correlati