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不知道这一点,但在以后的帖子中注意到。。。这是一个很好的代码,所以我忍不住;)