布尔代数表达在C#中的表达转换
我正在努力将布尔代数表达式转换为这样的东西
NOT(a AND b) = (NOT a) OR (NOT b)
NOT (a OR b) = (NOT a) AND (NOT b)
a AND NOT (b AND c) = (EQ a) AND ( ( NOT b) OR ( NOT c) )
,但是,以某种方式在某些条件下不起作用,例如
NOT a AND b = (NOT a) AND (EQ b)
a AND NOT b AND NOT c = (EQ a) AND (NOT b) AND (NOT c)
以下是转换的逻辑。我做错了吗?
public string ExpressionConversion(string expression)
{
string finalVal = null;
string currentToken = "";
bool isNotFound = false;
List<string> strList = new List<string>();
StringHelper stringHelper = new StringHelper();
var values = stringHelper.CleverSplit(expression, ' ', false, true); //function which splits all the elements in the expression
for (int j = 0; j < values.Length; j++)
{
currentToken = values[j].Trim();
if (string.IsNullOrEmpty(currentToken.Trim()))
continue;
if ((j > 0) && currentToken.StartsWith("AND") && values[j - 1].StartsWith("AND"))
continue;
if (currentToken.Contains("NOT"))
{
isNotFound = true;
continue;
}
if (currentToken.StartsWith("("))
strList.Add(currentToken);
else if (currentToken.StartsWith(")"))
{
strList.Add(currentToken);
isNotFound = false;
}
else if (currentToken.StartsWith("AND"))
{
if (isNotFound)
strList.Add(" OR ");
else
strList.Add(" AND ");
}
else if (currentToken.StartsWith("OR"))
{
if (isNotFound)
strList.Add(" AND ");
else
strList.Add(" OR ");
}
else
{
if (isNotFound)
{
strList.Add("( NOT " + currentToken + " )");
if (!expression.Contains("("))
isNotFound = false;
}
else
strList.Add("( EQ " + currentToken + " )");
}
}
if (strList.Count > 0)
finalVal = string.Join(" ", strList);
return finalVal;
}
I am working on converting the boolean algebra expression something like this
NOT(a AND b) = (NOT a) OR (NOT b)
NOT (a OR b) = (NOT a) AND (NOT b)
a AND NOT (b AND c) = (EQ a) AND ( ( NOT b) OR ( NOT c) )
But, somehow it is not working for certain conditions like
NOT a AND b = (NOT a) AND (EQ b)
a AND NOT b AND NOT c = (EQ a) AND (NOT b) AND (NOT c)
Following is the logic for conversion. Am i doing anything wrong?
public string ExpressionConversion(string expression)
{
string finalVal = null;
string currentToken = "";
bool isNotFound = false;
List<string> strList = new List<string>();
StringHelper stringHelper = new StringHelper();
var values = stringHelper.CleverSplit(expression, ' ', false, true); //function which splits all the elements in the expression
for (int j = 0; j < values.Length; j++)
{
currentToken = values[j].Trim();
if (string.IsNullOrEmpty(currentToken.Trim()))
continue;
if ((j > 0) && currentToken.StartsWith("AND") && values[j - 1].StartsWith("AND"))
continue;
if (currentToken.Contains("NOT"))
{
isNotFound = true;
continue;
}
if (currentToken.StartsWith("("))
strList.Add(currentToken);
else if (currentToken.StartsWith(")"))
{
strList.Add(currentToken);
isNotFound = false;
}
else if (currentToken.StartsWith("AND"))
{
if (isNotFound)
strList.Add(" OR ");
else
strList.Add(" AND ");
}
else if (currentToken.StartsWith("OR"))
{
if (isNotFound)
strList.Add(" AND ");
else
strList.Add(" OR ");
}
else
{
if (isNotFound)
{
strList.Add("( NOT " + currentToken + " )");
if (!expression.Contains("("))
isNotFound = false;
}
else
strList.Add("( EQ " + currentToken + " )");
}
}
if (strList.Count > 0)
finalVal = string.Join(" ", strList);
return finalVal;
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
如果您去适当的解析器,您的生活可能最容易。
注意:这里的错误处理非常可怕,并且没有单位测试。您需要改进这些!
我们首先将输入标记为
(
,),变量,操作员等。首先,让我们定义我们的令牌集。我们将使用一组所有实现相同标记接口的类,并将使用Singleton实例,在该实例中,令牌无需保留任何其他信息。我还将作弊并在操作员令牌上放置优先级和关联信息,我们将在一分钟内使用:
通过定义的,我们的令牌器非常简单:
我们只是浏览输入,在每个步骤中匹配正则是。正则吞咽了空格(因此我们不需要修剪自己)。任何不是我们已知的关键字之一的东西都是一个变量:这意味着您可以具有称为Eg
+
的变量。我们的Tokanizer可以将字符串Eg
A和(B和B)
解析到令牌a
,和,((,b
,和
,不是
,c
,)
。现在,我们想将其解析为AST。一种相当简单的方法是首先使用“ nofollow noreferrer”> >。
在后缀表达式中,操作员在操作数之后出现。因此,
A + B
变为ab +
。为了处理此操作,您会做一些堆栈。当您看到操作数时,将其推到堆栈上。当您看到操作员时,您会弹出多大参数,应用操作员,然后将结果推到堆栈上。因此,表达式
a而不是(不是b和c)
变为ab而不是c,而不是
,即:[a]
b
[a,b]
不是
[a,not b]
c
[a,not b,c]
和
a,(不是b)和c]
a,不是(不是b和c)]
和
a而不是(不是B和C)]
关于此事的可爱之处在于,您不必担心括号:您只需通过令牌即可在后缀表达式代币中行走并随身携带。
这几乎是 algorithm on wikipedia )。
唯一的复杂性是您需要支持Eg
A B
或a而不是b
的输入,而不是表示a和b
and b anda和
b
分别。分流码算法无法应付本地:它期望操作数之间的操作员。因此,我们将其入侵:如果我们看到一个直接遵循另一个变量的变量或而不是
,我们将假装首先看到and
。下一步涉及将其纳入AST。我们将使用一棵树,其中运算符和变量都由节点表示。一个变量节点没有任何孩子:它只是知道变量名称。单一操作员(
不是
或eq
)有一个孩子。二进制运算符(和
或或
)有两个孩子,这是两件事是被安排或或者在一起的。我们将以与代币相同的方式定义这些定义,并实现大量类的类。
我们还将添加一个
倒置
方法:在节点上实现时,它将返回该节点的倒置版本,并应用de Morgan的定律。因此,反转eq
节点会导致与倒置子的节点
节点(反之亦然);反转和
或或分别以或
/和
的形式导致其两个孩子倒置。这意味着反相
eq a
导致而不是
,反转eq a和eq b
导致in
而不是a或b
代码>等。就此,我们可以编写我们的小方法来生成AST。这几乎可以用我上面描述的堆栈来完成。但是,每次看到变量令牌时,我们都会在
eq
节点中潜入节点 - 因此,将始终使用指向它的eq
节点创建变量。当我们看到而不是
令牌时,我们将仅将当前堆栈上的任何内容转动:如果是eq
节点,它将自身变成not
not < /代码>节点。
最后,我们需要将AST渲染到字符串。我们将简单地递归依次访问每个节点来做到这一点。
(不是&lt;访问Child&gt;)
。这意味着:
将其呈现为
a和b
,但:以
a和(b and c)
渲染。就是这样!将它们拼接在一起:
在dotnetfiddle.net.net上看到它。
Your life's probably going to be easiest if you go for a proper parser.
Note: the error handling in here is pretty dreadful, and there are no unit tests. You'll want to improve these!
We'll first tokenise the input, producing tokens for the
(
,)
, variables, operators, etc.First, let's define our set of tokens. We'll use a set of classes which all implement the same marker interface, and we'll use a singleton instance where the token doesn't need to hold any further information. I'm also going to cheat and put precedence and associativity information on the operator tokens, which we'll use in a minute:
With those defined, our tokenizer is pretty simple:
We just walk through the input, matching a regex at each step. The regex swallows whitespace (so we don't need to trim that ourselves). Anything which isn't one of our known keywords is a variable: this means you could have a variable called e.g.
+
.Our tokanizer can parse the string e.g.
a AND (b AND NOT b)
into the tokensa
,AND
,(
,b
,AND
,NOT
,c
,)
. Now we want to parse that into an AST.One fairly simply way to do is to first transform it into postfix using Dijkstra's Shunting-yard Algorithm.
In a postfix expression, the operators appear after their operands. So
a + b
becomesa b +
. To process this, you make a little stack. When you see an operand, you push it onto the stack; when you see an operator, you pop off however many arguments it takes, apply the operator, and push the result onto the stack.So the expression
a AND NOT (NOT b AND c)
becomesa b NOT c AND NOT AND
, that is:a
[a]
b
[a, b]
NOT
[a, NOT b]
c
[a, NOT b, c]
AND
a, (NOT b) AND c]
NOT
a, NOT (NOT b AND c)]
AND
a AND NOT (NOT b AND c)]
The lovely thing about this is that you don't need to worry about parentheses: you can just walk the postfix expression token by token and evaluate it as you go.
This is pretty much a direct port of the algorithm on Wikipedia.
The only complexity is that you need to support inputs of e.g.
a b
ora NOT b
to meana AND b
anda AND NOT b
respectively. The shunting-yard algorithm can't cope with this natively: it's expecting an operator between operands. So we hack this in: if we see a variable or aNOT
which directly follows another variable, we'll pretend that we saw anAND
first.The next step involves turing this into an AST. We'll use a tree, where operators and variabls are all represented by nodes. A variable node doesn't have any children: it just knows the variable name. A unary operator (
NOT
orEQ
) has a single child. A binary operator (AND
orOR
) has two children, which are the two things being anded or or'd together.We'll define these in the same way as the tokens, with a bunch of classes implementing a common interface.
We'll also add an
Invert
method: when implemented on a node, this will return an inverted version of the node, applying De Morgan's laws. So inverting anEQ
node results in aNOT
node with an inverted child (and vice versa); inverting anAND
orOR
results in anOR
/AND
respectively, with both of its children inverted.This means that inverting
EQ A
results inNOT A
, invertingEQ A AND EQ B
results inNOT A OR NOT B
, etc.With that in place, we can write our little method to generate the AST. This does pretty much exactly what I described above, with a stack. We however sneak in an
EQ
node every time we see a variable token -- so a variable will always be created with anEQ
node pointing to it. When we see aNOT
token, we'll just invert whatever's currently on the stack: if it's anEQ
node, it'll turn itself into aNOT
node.Finally we need to render our AST to a string. We'll do this simply by recursively visiting each node in turn.
(NOT <visit child>)
.This means that:
gets rendered as
a AND b
, but:gets rendered as
a AND ( b AND c )
.And that's pretty much it! Stitch it all together:
See it on dotnetfiddle.net.