以下是关于如何解释此问题的后续问题。结果证明它比公认的答案更强大:Extract All Possible Paths from Expression-Tree and evaluate them to hold TRUE
我在运行时从字符串中获得了一些逻辑表达式:
"100 AND 101"
"102 AND 103 AND 104 AND 105"
"(106 AND 107 AND 108) OR (109 AND 110)"
"111 AND (NOT( 112 AND 113 ))" // NOT is encapsulated in its own group if it is combined
操作类型为:
AND
用于连接/ logical-and OR
用于disjunction / logical-或NOT
for inversion / logical.not 操作数是一些Id表示为整数。
我假设表达式字符串为:
100 AND 101 OR 102 AND 103
)如果此表达式的计算结果为true
或false
,我需要解析此字符串并根据ID列表进行检查。
我目前的结构是:
public class ExpressionTree
{
public enum OperationTypes { Not , And , Or }
public static Dictionary<string , OperationTypes> OperationTypeMap = new Dictionary<string , OperationTypes> {
{ "NOT" , OperationTypes.Not } ,
{ "AND" , OperationTypes.And } ,
{ "OR" , OperationTypes.Or } ,
};
public class Group
{
public OperationTypes OperationType;
public Group Parent;
public List<Group> Groups = new List<Group>();
public List<Identifier> Identifiers = new List<Identifier>();
}
public class Identifier
{
public OperationTypes OperationType;
public Group Parent;
public int Id;
}
public Group Root;
}
然而,这种结构并不像我希望的那样直观,而且它的评价也很麻烦。 主要焦点应该是易于理解,性能不是一个问题。
这种表达式是否有更优化的类结构更容易解释?
这是一个工作示例(可能还有错误)
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
namespace VisitorPattern
{
public class Test
{
public static void Main2( string[] args )
{
var list = new Dictionary<string , List<int>> {
{ "({100} OR {101} OR {102})" , new List<int> { 100 } } ,
{ "(NOT ({100} OR {101} OR {102}))" , new List<int> { 0 } } ,
{ "{100} AND {101} AND {102}" , new List<int> { 100 , 101 , 102 } } ,
{ "{100} OR {101} OR {102}" , new List<int> { 101 } } ,
{ "NOT ({100})" , new List<int> { 0 } } ,
{ "{100} AND (NOT ({101} OR {102} OR {103}))" , new List<int> { 100 , 104 } } ,
{ "({100} AND ({101} OR {102} OR {103})) AND ({104} OR {105} OR {106})" , new List<int> { 100 , 103 , 104 } } ,
};
foreach( var elem in list )
{
var processor = new Processor();
var ruleVisitor = new RuleVisitor { Rules = elem.Value };
var node = processor.Parse( elem.Key );
var result = ruleVisitor.Visit( node , true );
Debug.Assert( result );
}
}
}
public interface IVisitor<T>
{
T Visit( OrNode node , bool result );
T Visit( NotNode node , bool result );
T Visit( AndNode node , bool result );
T Visit( GroupNode node , bool result );
T Visit( IdentifierNode node , bool result );
}
public class RuleVisitor : IVisitor<bool>
{
public List<int> Rules;
public bool Visit( OrNode node , bool result )
{
return node.SubNodes.First().Accept( this , result ) || result;
}
public bool Visit( AndNode node , bool result )
{
return node.SubNodes.First().Accept( this , result ) && result;
}
public bool Visit( NotNode node , bool result )
{
return !node.SubNodes.First().Accept( this , result );
}
public bool Visit( GroupNode node , bool result )
{
return node.SubNodes.Aggregate( result , ( res , child ) => child.Accept( this , res ) );
}
public bool Visit( IdentifierNode node , bool result )
{
return this.Rules.Contains( node.Identifier );
}
}
#region Node Types
public abstract class NodeBase
{
public List<NodeBase> SubNodes = new List<NodeBase>();
public abstract T Accept<T>( IVisitor<T> visitor , bool result );
}
public class OrNode : NodeBase
{
public override T Accept<T>( IVisitor<T> visitor , bool result )
{
return visitor.Visit( this , result );
}
}
public class NotNode : NodeBase
{
public override T Accept<T>( IVisitor<T> visitor , bool result )
{
return visitor.Visit( this , result );
}
}
public class AndNode : NodeBase
{
public override T Accept<T>( IVisitor<T> visitor , bool result )
{
return visitor.Visit( this , result );
}
}
public class GroupNode : NodeBase
{
public override T Accept<T>( IVisitor<T> visitor , bool result )
{
return visitor.Visit( this , result );
}
}
public class IdentifierNode : NodeBase
{
public int Identifier;
public override T Accept<T>( IVisitor<T> visitor , bool result )
{
return visitor.Visit( this , result );
}
}
#endregion
public class Processor
{
public enum OperationTypes
{
Not,
And,
Or,
}
public static Dictionary<string , OperationTypes> OperationTypeMap = new Dictionary<string , OperationTypes> {
{ "NOT" , OperationTypes.Not } ,
{ "AND" , OperationTypes.And } ,
{ "OR" , OperationTypes.Or } ,
};
public GroupNode Parse( string ruleString )
{
var index = 0;
var rootNode = new GroupNode();
rootNode.SubNodes = this.Parse( ruleString , ref index );
return rootNode;
}
private List<NodeBase> Parse( string ruleString , ref int index )
{
var node = default( NodeBase );
var nodes = new List<NodeBase>();
for( ; index < ruleString.Length ; index++ )
{
var c = ruleString[ index ];
if( char.IsWhiteSpace( c ) )
{
continue;
}
else if( char.IsLetter( c ) )
{
var opType = this.ScanOperationType( ruleString , ref index );
if( opType == OperationTypes.And )
node = new AndNode();
else if( opType == OperationTypes.Or )
node = new OrNode();
else if( opType == OperationTypes.Not )
node = new NotNode();
nodes.Add( node );
}
else if( c == '(' )
{
if( node == null )
{
node = new GroupNode();
nodes.Add( node );
}
index++;
node.SubNodes.Add( new GroupNode {
SubNodes = this.Parse( ruleString , ref index )
} );
}
else if( c == ')' )
{
index++;
break;
}
else if( c == '{' )
{
var idNode = new IdentifierNode {
Identifier = this.ScanNumber( ruleString , ref index )
};
if( node == null )
{
node = idNode;
nodes.Add( idNode );
}
else
{
node.SubNodes.Add( idNode );
}
}
else
{
Debug.Fail( "" );
}
}
return nodes;
}
private OperationTypes ScanOperationType( string ruleString , ref int index )
{
var idx = index;
var operationType = OperationTypeMap
.Where( x => ruleString.Length > x.Key.Length + idx - 1 )
.Where( x => ruleString.Substring( idx , x.Key.Length ) == x.Key )
.Single();
index += operationType.Key.Length;
return operationType.Value;
}
private int ScanNumber( string ruleString , ref int index )
{
var sb = new StringBuilder();
for( var c = ruleString[ ++index ] // skip opening brace
; index < ruleString.Length
; c = ruleString[ ++index ]
)
{
if( char.IsNumber( c ) )
sb.Append( c );
else if( c == '}' )
break;
else
Debug.Fail( "" );
}
return Convert.ToInt32( sb.ToString() );
}
}
}
答案 0 :(得分:3)
您可以想象,您的所有实体(NOT
,OR
,AND
,ID
)都来源于Node
之类的共同抽象,执行一些基本操作,例如枚举子节点等。然后,如果您有相对较小且固定的节点集,则可以申请类模式Visitor的层次结构。因此,您的代码可能如下所示:
public interface Visitor<T>
{
T Visit(OrNode node);
T Visit(NotNode node);
T Visit(AndNode node);
T Visit(IdNode node);
}
public abstract class ANode
{
public List<ANode> Childs { get; }
public abstract T Accept<T>(Visitor<T> visitor);
}
public class OrNode : ANode
{
public override T Accept<T>(Visitor<T> visitor)
{
return visitor.Visit(this);
}
}
public class NotNode : ANode
{
public override T Accept<T>(Visitor<T> visitor)
{
return visitor.Visit(this);
}
}
public class AndNode : ANode
{
public override T Accept<T>(Visitor<T> visitor)
{
return visitor.Visit(this);
}
}
public class IdNode : ANode
{
public bool Value;
public override T Accept<T>(Visitor<T> visitor)
{
return visitor.Visit(this);
}
}
您可以使用下一个访问者计算此表达式的任何子树的值:
public class ValueVisitor : Visitor<bool> {
public bool Visit(OrNode node) {
return node.Childs.Aggregate(false, (current, child) => current | child.Accept(this));
}
public bool Visit(NotNode node) {
return !node.Childs[0].Accept(this);
}
public bool Visit(AndNode node) {
return node.Childs.Aggregate(true, (current, child) => current & child.Accept(this));
}
public bool Visit(IdNode node) {
return node.Value;
}
}
为了方便地将文本解析为此类结构,您可以在C#中使用Monadic-Parser-Combinators
Sprache。