Algorithm 如何在Scala中简化表达式(and、or、not)?
我不知道如何解决我的问题。我有一个家庭作业,我需要使用教授规定的规则简化表达式。我需要获取表达式的字符串:Algorithm 如何在Scala中简化表达式(and、or、not)?,algorithm,scala,recursion,Algorithm,Scala,Recursion,我不知道如何解决我的问题。我有一个家庭作业,我需要使用教授规定的规则简化表达式。我需要获取表达式的字符串: "(and(x x))" "(or (and x z) y)" "(and (or z (not x))(or e a))" "(and (or (and x y) z)(or x a))" "(and (or (and x y) z)(or (and y z) a))" scala> case object True extends Expr defined module Tr
"(and(x x))"
"(or (and x z) y)"
"(and (or z (not x))(or e a))"
"(and (or (and x y) z)(or x a))"
"(and (or (and x y) z)(or (and y z) a))"
scala> case object True extends Expr
defined module True
scala> case object False extends Expr
defined module False
scala> case class Var(name:String) extends Expr { override def toString = name }
defined class Var
scala> val X = Var("X"); val Y = Var("Y"); val A = Var("A"); val B = Var("B")
X: Var = X
Y: Var = Y
A: Var = A
B: Var = B
scala> And(X, True).simplify
res10: Expr = X
scala> And(X, And(Y, False)).simplify
res11: Expr = False
scala> And(X, Or(Y, False)).simplify
res12: Expr = And(X,Y)
scala> Or(True, And(X, Or(Y, False))).simplify
res13: Expr = True
并使用规则简化它们:
(or x nil) => x;
(or nil x) => x;
(or 1 x) => 1;
(or x 1) => 1;
(and x nil) => nil;
(and nil x) => nil;
(and x 1) => x;
(and 1 x) => x;
(not nil) => 1;
(not 1) => nil;
(not (and x y)) => (or (not x) (not y));
(not (or x y)) => (and (not x) (not y));
我决定采用上面的精确形式(不能是任何其他方式),并将其解析为一个数组,因此在每个索引中,例如,它将如下所示:
and or x y z //ArrayBuffer[String]
然后我使用一个递归函数检查左表达式和右表达式,直到得到简化表达式。我的问题不在于规则,我已经明白了。我基本上完成了3个案例,分别是:
"(and z (or x y)" // the case when the left symbol is simple but the right side must be recursed
"(and (or x y) z)" // case when the right symbol is simple but the right side must be recursed
"(and x y)" // simple case where no recursion is necessary
我忽略了这样一种情况,即必须递归左符号和右符号才能获得这些简化符号。我无法知道它们何时结束或开始,在许多情况下,甚至在那些内部表达式中,它也必须递归:
"(and(x x))"
"(or (and x z) y)"
"(and (or z (not x))(or e a))"
"(and (or (and x y) z)(or x a))"
"(and (or (and x y) z)(or (and y z) a))"
scala> case object True extends Expr
defined module True
scala> case object False extends Expr
defined module False
scala> case class Var(name:String) extends Expr { override def toString = name }
defined class Var
scala> val X = Var("X"); val Y = Var("Y"); val A = Var("A"); val B = Var("B")
X: Var = X
Y: Var = Y
A: Var = A
B: Var = B
scala> And(X, True).simplify
res10: Expr = X
scala> And(X, And(Y, False)).simplify
res11: Expr = False
scala> And(X, Or(Y, False)).simplify
res12: Expr = And(X,Y)
scala> Or(True, And(X, Or(Y, False))).simplify
res13: Expr = True
我已经考虑过如何用当前的实现高效地完成这项工作,但还没有得到任何结果。我想就如何着手这件事征求一些建议。我没有提供任何代码,因为我想自己完成它,只需要在正确的方向上轻推。如果需要澄清,请询问,我会这样做。再次感谢你
and or x y z //ArrayBuffer[String]
我会避免这样的表达。虽然可以在前缀概念中毫不含糊地获得表达式的结构,但这并不像使用递归结构那么容易
您应该使用递归定义的类层次结构来表示表达式。在不提供太多细节的情况下,您可能会有一个接口(Trait或abstract类),该接口的实现者取决于参数的数量:一个用于包含三个部分的表达式(例如((或,x,y)
,或(和,(或xy),z))
,一个用于包含两个部分的表达式(比如(不是,x)
),一个表示带有一个部分的表达式(比如x
,y
,z
,nil
,等等)
然后,简化过程就变成了一个大模式匹配方法,它可以递归调用自身来遍历表达式的解析树:
def simplify(expression: ExpressionIterface) =
expression match {
case /* pattern */ => /* result, possibly with a recursive call to simplify */
...
}
编辑:转换
ArrayBuffer[String]
进入类可以通过简单的递归解析函数完成,因为您知道每个运算符应该关联多少个参数。您可以遍历缓冲区,每次看到和
或或
时,您就开始创建一个由三部分组成的表达式,每次看到非
时,您就开始创建一个由两部分组成的表达式,您可以为其他任何内容创建一个由一部分组成的表达式。以下是dhg解决方案的一个简单实现:
package solve
sealed trait Expr
case class Not(e:Expr) extends Expr
case class And(e1:Expr, e2:Expr) extends Expr
case class Or(e1:Expr, e2:Expr) extends Expr
case class Idn(v:String) extends Expr
object Solve extends App {
def prep(s:String):List[String] =
s.replaceAll("[()]+"," ").split(" ").filter(_.size > 0).toList
def parse(l:List[String]):Expr =
parseI(l) match {
case (e,Nil) => e
case _ => throw new Exception("malformed exception")
}
def parseI(l:List[String]):(Expr,List[String]) =
l match {
case "not" :: rest =>
val (e, rem) = parseI(rest)
(Not(e), rem)
case "and" :: rest =>
val (e1, rem) = parseI(rest)
val (e2, rem2) = parseI(rem)
(And(e1,e2), rem2)
case "or" :: rest =>
val (e1, rem) = parseI(rest)
val (e2, rem2) = parseI(rem)
(Or(e1,e2), rem2)
case i :: rest =>
(Idn(i), rest)
case Nil => throw new Exception
}
def simplify(e:Expr):Expr = {
e match {
case Or(x,Idn("nil")) => simplify(x)
case Or(Idn("1"),x) => Idn("1")
case Or(x,y) => Or(simplify(x),simplify(y))
case x => x
}
}
}
以及它的一个测试用例:
import org.scalatest.FunSuite
import org.scalatest.matchers.ShouldMatchers
import solve._
import Solve._
class SolveTest extends FunSuite with ShouldMatchers {
test ("prepare expression") {
prep("(and(x x))") should equal (List("and","x","x"))
}
test ("parse expressions") {
parse(prep("(and(x x))")) should equal (And(Idn("x"), Idn("x")))
parse(prep("(or (and x z) y)")) should equal (Or(And(Idn("x"), Idn("z")), Idn("y")))
parse(prep("(and (or z (not x))(or e a))")) should equal (And(Or(Idn("z"),Not(Idn("x"))),Or(Idn("e"),Idn("a"))))
}
test ("simplification") {
simplify(parse(prep("(or (and x z) nil)"))) should equal (And(Idn("x"),Idn("z")))
}
}
我认为scala lang网站(见第7章)中已经介绍了这一点。我的建议是使用case类来表示表达式,然后使用模式匹配来简化表达式:
"(and(x x))"
"(or (and x z) y)"
"(and (or z (not x))(or e a))"
"(and (or (and x y) z)(or x a))"
"(and (or (and x y) z)(or (and y z) a))"
scala> case object True extends Expr
defined module True
scala> case object False extends Expr
defined module False
scala> case class Var(name:String) extends Expr { override def toString = name }
defined class Var
scala> val X = Var("X"); val Y = Var("Y"); val A = Var("A"); val B = Var("B")
X: Var = X
Y: Var = Y
A: Var = A
B: Var = B
scala> And(X, True).simplify
res10: Expr = X
scala> And(X, And(Y, False)).simplify
res11: Expr = False
scala> And(X, Or(Y, False)).simplify
res12: Expr = And(X,Y)
scala> Or(True, And(X, Or(Y, False))).simplify
res13: Expr = True
首先,让我们定义一个表达式特征,用于表示各种表达式
scala> trait Expr { def simplify:Expr = this }
defined trait Expr
这里,我让Expr trait实现了一个默认的simplify
方法,该方法只返回扩展trait的对象
"(and(x x))"
"(or (and x z) y)"
"(and (or z (not x))(or e a))"
"(and (or (and x y) z)(or x a))"
"(and (or (and x y) z)(or (and y z) a))"
scala> case object True extends Expr
defined module True
scala> case object False extends Expr
defined module False
scala> case class Var(name:String) extends Expr { override def toString = name }
defined class Var
scala> val X = Var("X"); val Y = Var("Y"); val A = Var("A"); val B = Var("B")
X: Var = X
Y: Var = Y
A: Var = A
B: Var = B
scala> And(X, True).simplify
res10: Expr = X
scala> And(X, And(Y, False)).simplify
res11: Expr = False
scala> And(X, Or(Y, False)).simplify
res12: Expr = And(X,Y)
scala> Or(True, And(X, Or(Y, False))).simplify
res13: Expr = True
True
和False
将在您的示例中表示1
和nil
。Var
将用于表示尚未具有真值的变量,例如,在您的示例中为x、y、a和b。Var还覆盖了toString
方法,以使打印输出更加美观:)
现在来看看和/或的更棘手的情况。让我们定义如下:
scala> case class And(a:Expr, b:Expr) extends Expr {
| override def simplify = (a.simplify, b.simplify) match {
| case (True,x) => x
| case (x,True) => x
| case (False,x) => False
| case (x,False) => False
| case (x,y) => And(x,y)
| }
| }
defined class And
scala> case class Or(a:Expr, b:Expr) extends Expr {
| override def simplify = (a.simplify, b.simplify) match {
| case (True,x) => True
| case (x,True) => True
| case (False,x) => x
| case (x,False) => x
| case (x,y) => Or(x,y)
| }
| }
defined class Or
和
以及或
都覆盖了Expr
trait中的simplify
方法,并返回其自身及其子表达式的简化版本。现在,这些表达式可以与更简单的True、False和Var表达式一起用于构建表达式:
"(and(x x))"
"(or (and x z) y)"
"(and (or z (not x))(or e a))"
"(and (or (and x y) z)(or x a))"
"(and (or (and x y) z)(or (and y z) a))"
scala> case object True extends Expr
defined module True
scala> case object False extends Expr
defined module False
scala> case class Var(name:String) extends Expr { override def toString = name }
defined class Var
scala> val X = Var("X"); val Y = Var("Y"); val A = Var("A"); val B = Var("B")
X: Var = X
Y: Var = Y
A: Var = A
B: Var = B
scala> And(X, True).simplify
res10: Expr = X
scala> And(X, And(Y, False)).simplify
res11: Expr = False
scala> And(X, Or(Y, False)).simplify
res12: Expr = And(X,Y)
scala> Or(True, And(X, Or(Y, False))).simplify
res13: Expr = True
最后,我们为not添加了一个表达式:
scala> case class Not(a:Expr) extends Expr {
| override def simplify = a.simplify match {
| case True => False
| case False => True
| case And(x,y) => Or(Not(x),Not(y))
| case Or(x,y) => And(Not(x),Not(y))
| case Not(x) => x
| case x => Not(x)
| }
| }
defined class Not
现在我们可以在您的示例中表示表达式。但是,对于某些表达式,该Not case类不会进行完全简化,例如
scala> Not(Or(Not(X),Y)).simplify
res41: Expr = And(Not(Not(X)),Not(Y))
因此,我们可以在Not
中定义一个递归函数,该函数试图简化表达式,直到无法再简化它:
scala> case class Not(a:Expr) extends Expr {
| override def simplify = recursiveSimplify(a, a)
| private def recursiveSimplify(curExpr:Expr, lastExpr:Expr):Expr = if(curExpr != lastExpr) {
| val newExpr = curExpr.simplify match {
| case True => False
| case False => True
| case Var(x) => Not(Var(x))
| case Not(x) => x
| case And(x,y) => Or(Not(x), Not(y))
| case Or(x,y) => And(Not(x), Not(y))
| }
| recursiveSimplify(newExpr, curExpr)
| } else {
| lastExpr
| }
| }
defined class Not
现在,前面的表达式简化为:
scala> Not(Or(Not(X),Y)).simplify
res42: Expr = Or(Not(X),Y)
起初我走的是那条路线,但后来遇到了一个问题,所以我认为这样做会更快,以便按时交上来。有没有关于如何保持我的方式的提示?如果不是的话,那很好,我将不得不切换实现。但是我不想扔掉我想出的所有代码!但是我很感激您的输入。@Andy,在部件的平面列表中查找模式基本上需要重新解析才能找到结构。但是,您可以将平面列表转换为层次表达式(请参阅我的编辑以了解大致的详细信息)。对此的另一个改进可能是将
simplify
方法中的代码提取到一个Simplifier类中,该类只处理所有不同的情况。这样,如果您添加了一个新的表达式,例如implies、xor等,模型(即Expr
子类)就不需要更改。@dhg不知道这一点,但在以后的帖子中注意到。。。这是一个很好的代码,所以我忍不住;)