我受到启发,使用反向抛光表示法作为我将要教授的课程的解析器组合器的示例,但是,我的解决方案最终使用类型List[Any]
分别存储浮点数和二元运算符。最后,我递归地解构列表并在遇到它时应用二元运算符。整个实现在这里:
import scala.util.parsing.combinator._
trait Ops {
type Op = (Float,Float) => Float
def add(x: Float, y: Float) = x + y
def sub(x: Float, y: Float) = x - y
def mul(x: Float, y: Float) = x * y
def div(x: Float, y: Float) = x / y
}
trait PolishParser extends Ops with JavaTokenParsers {
// Converts a floating point number as a String to Float
def num: Parser[Float] = floatingPointNumber ^^ (_.toFloat)
// Parses an operator and converts it to the underlying function it logically maps to
def operator: Parser[Op] = ("*" | "/" | "+" | "-") ^^ {
case "+" => add
case "-" => sub
case "*" => mul
case "/" => div
}
}
trait PolishSemantics extends PolishParser {
def polish:Parser[Float] = rep(num | operator) ^^ ( xs => reduce(xs).head )
def pop2(xs:List[Float],f:Op) = (xs take 2 reduce f) :: (xs drop 2)
def reduce(input:List[Any],stack:List[Float] = Nil):List[Float] = input match {
case (f:Op) :: xs => reduce(xs,pop2(stack,f))
case (x:Float) :: xs => reduce(xs,x :: stack)
case Nil => stack
case _ => sys.error("Unexpected input")
}
}
class PolishInterpreter extends PolishParser with PolishSemantics {
// Parse an expression and return the calculated result as a String
def interpret(expression: String) = parseAll(polish, expression)
}
object Calculator extends PolishSemantics {
def main(args: Array[String]) {
val pi = new PolishInterpreter
println("input: " + args(0))
println("result: " + pi.interpret(args(0)))
}
}
我想要实现的是不在reduce函数中使用type-pattern。一种解决方案当然是按照以下意义制作自定义类型层次结构:
trait Elem
case class Floating(f:Float) extends Elem
case class Operator(o: (Float,Float) => Float) extends Elem
通过这种方式,我可以通过unapply方法在case-classes上使用模式匹配,但这也需要对代码进行大量重构。 另一种方法可能是在解析时直接应用语义,这将允许我只使用浮点数的“堆栈”,然后在解析它们之后立即处理运算符。这当然会完全破坏解析器 - 组合器工作的声明性方式,并且会成为对世界上所有好事的犯罪。
当然,我知道这是挑剔,但每个人都是一个试图离开的软件工程师,我准备好最后的建议,让这个例子变得完美。有任何想法吗? :)