解析命题逻辑程序中的括号

时间:2012-04-30 19:10:37

标签: java parsing recursion parentheses

我正在完成这项家庭作业,将命题逻辑陈述翻译成合一的正常形式。我遇到的问题是解析可能复杂的括号级别。这个问题需要以正确的顺序解决,我认为递归应该先用于解决内部括号,然后再向外移动,但我无法弄清楚如何实现我的想法。该程序需要用Java编写。有人可以帮忙吗?

3 个答案:

答案 0 :(得分:1)

您能举例说明您要转换为CNF的声明吗?我在想你的意思是在起始逻辑语句中有括号,但它会有所帮助。

与此同时,我会说你不需要递归......但是堆栈会非常有用:)将项目及其数学运算推送到堆栈上,然后将它们弹出并在适当的时候对它们进行操作。由于它的功课我不会详细介绍,但是你会发现你将推 - 推 - 推 - 弹 - 乘 - 推 - 推 - 弹 - 加 - 弹 - 除......使用堆栈作为一种关注当前操作的方法。

对Postfix Notation的调查也很有用,可能有所帮助......即使维基百科上的文章也会给你一些想法(尽管那里有更多以计算机科学为导向的文章)。

免责声明:我没有考虑我的例子中那些推送和弹出的数量或顺序:)


更新

您不需要多次传递数据,并且可以即时转换为CNF。

你正朝着正确的方向前进,所以有些帮助/提示:

  • 使用两个堆栈,一个用于操作数,一个用于操作符。
  • 如果有两个操作数和一个运算符,则弹出操作数,应用运算符,并将结果作为新的(合并的)操作数推回
  • 后缀的优点是你可以动态进行转换...例如,遇到→时,将¬应用于操作数堆栈并将push推入操作员堆栈

减少你的榜样:

F→(E⋀(A→B))

转换的第一步看起来像这样(我假设您已经深入了解了基础逻辑转换规则,并且它只是您正在编写的代码):

F→(E⋀(A→B))
¬F⋁(E⋀(A→B))
¬F⋁(E⋀(¬A⋁B))

在CNF后缀中,它将如下所示:

F¬EA¬B⋁⋀⋁     // (Parentheses are obviated in postfix notation)

要实现这一目标,请从左到右一次读取初始逻辑语句...将操作数推送到操作数堆栈,将操作符推送到操作符堆栈。

在应用运算符时,从操作数堆栈中弹出所需的操作数,应用运算符,并将生成的字符串作为新操作数推回堆栈。

结束括号或低优先级操作会触发pop-apply-push。整件事看起来像这样:

                                      Operand        Operator
                                       Stack          Stack
                                     ----------     ----------
Read F:  Push onto Operand stack     F ........      ..........

Read →:  On-the-fly conversion (→ becomes ¬⋁)
  Unary Operator ¬ pops F from Operands; applies it; pushes back to Operands
                                     ¬F .......      ..........   
  Push ⋁ onto the op-stack           ¬F .......      ⋁ .......    

Read (:  Discard    

Read E:  Push onto Operands stack    ¬F E ....       ⋁ ...... 
Read ⋀:  Push onto Operators stack   ¬F E ....       ⋁⋀ .....

Read (:  Discard                    
Read A:  Push onto Operands stack    ¬F E A ..       ⋁⋀ .....

Read →:  On-the-fly conversion (→ becomes ¬⋁)
  Unary Operator ¬ pops A from Operands; applies; pushes back to Operands
                                     ¬F E ¬A .       ⋁⋀ .....
  Push ⋁ onto Operators stack        ¬F E ¬A .       ⋁⋀⋁ ....

Read B:  Push onto Operands stack    ¬F E ¬A B       ⋁⋀⋁ ....

Read ):  Triggers Operators/Operands pop; applies; pushes back to Operands 
        (After, there are three operands on the Operands stack)
                                     ¬F E (¬A⋁B)    ⋁⋀ ....

Read ):  Triggers Operators/Operands pop; applies; pushes back to Operands
        (After, there are two operands on the Operands stack)
                                     ¬F (E⋀(¬A⋁B))  ⋁ ....

No more reads:  Final Operators/Operands pop/apply/push (result is output)
                                    ¬F⋁(E⋀(¬A⋁B))  


警告和注释:

这只是一个正确方向的开始......你将不得不处理像运营商优先权这样的问题(即你先推动并采取行动,或者现在弹出并采取行动)。您会发现您的决定是基于您阅读的下一个字符(而不是在右括号中,正如我上面暗示的那样)。

这听起来比它复杂......上面的伪代码不会超过12-15行。但是,我还没有解决的复杂问题......< - >函数必须以类似于→高于......的方式建模......你必须采用这种思路并实现所有的转换规则。

隐含的括号可以让你绊倒,例如

6 * (8 * 6) + 4

真的是

(6 * (8 * 6)) + 4

如果您没有正确处理运算符优先级,最终可能会出现类似

的内容
686*4+*

这给你312,而不是正确答案(292)。

如果您在阅读本文中的unicode逻辑符号时遇到问题,请告诉我们;我可以使用标准字符重做它,但它在uni中读得更好:)

HTH,
詹姆斯


PS:一个漂亮的小程序,说明了后缀在这里:
http://fac-web.spsu.edu/cs/faculty/bbrown/web_lectures/postfix/

答案 1 :(得分:0)

伪代码:

parseRestOfString (int openParens, String rest) { 
    c = rest (0);
    if (c == ')' && openParens == 0) ...

这会给你一个提示吗?

答案 2 :(得分:0)