波兰语符号求值函数



我是 Scala 的新手,我很难定义,或者更有可能从 Ruby 翻译我的代码来评估描述为波兰符号的计算,F.E. (+ 3 2)(- 4 (+ 3 2))

我成功地将字符串解析为 ArrayBuffer(+, 3, 2)ArrayBuffer(-, 4, ArrayBuffer(+, 3 2)) 的形式。

当我尝试定义一个递归 eval 函数时,问题实际上就开始了,它只是将ArrayBuffer作为参数并"返回"一个Int(评估应用程序的结果(。

在基本情况下:我想简单地检查第二个元素是否是instanceOf[Int],第三个元素是否instanceOf[Int]然后一起评估它们(取决于符号运算符 - 第一个元素(并返回Int

但是,如果任何元素是另一个ArrayBuffer,我只想将该元素重新分配给递归称为 eval 函数的返回值。 Storage(2) = eval(Storage(2)) .(**这就是我使用可变数组缓冲区的原因**(

我得到的错误是:

scala.collection.mutable.ArrayBuffer cannot be cast to java.lang.Integer

我当然不是在寻找任何复制和粘贴的答案,而是寻求一些建议和观察。建设性的批评受到充分欢迎。

这是测试代码,仅用于添加

******
def eval(Input: ArrayBuffer[Any]):Int = {
  if(ArrayBuffer(2).isInstaceOf[ArrayBuffer[Any]]) {    
    ArrayBuffer(2) = eval(ArrayBuffer(2))
  }
  if(ArrayBuffer(3).isInstaceOf[ArrayBuffer[Any]]) {    
    ArrayBuffer(3) = eval(ArrayBuffer(3))
  }
  if(ArrayBuffer(2).isInstaceOf[Int] && ArrayBuffer(3).isInstanceOf[Int]) { 
    ArrayBuffer(2).asInstanceOf[Int] + ArrayBuffer(3).asInstanceOf[Int]
  }
}

代码存在一些问题:

  • ArrayBuffer(2)的意思是"用一个元素构建一个ArrayBuffer2"。 代码中没有任何地方引用参数Input 。 您需要将ArrayBuffer(2)实例替换为Input(2)才能正常工作。
  • ArrayBuffer(以及 Scala 中的所有集合(都是 0 索引的,所以如果你想访问集合中的第二件事,你会做input(1) .
  • 如果你把最后的if留在那里,那么编译器会抱怨,因为你的函数并不总是返回一个Int;如果输入包含一些意外的东西,那么最后一个if的计算结果将是false,你没有else可以落入。

以下是代码的直接重写:修复问题:

def eval(input: ArrayBuffer[Any]):Int = {
  if(input(1).isInstanceOf[ArrayBuffer[Any]])
    input(1) = eval(input(1).asInstanceOf[ArrayBuffer[Any]])
  if(input(2).isInstanceOf[ArrayBuffer[Any]])
    input(2) = eval(input(2).asInstanceOf[ArrayBuffer[Any]])
  input(1).asInstanceOf[Int] + input(2).asInstanceOf[Int]
}

(另请注意,变量名(如 input(应为小写。


也就是说,用评估替换输入中的条目的过程可能不是最佳途径,因为它会在评估过程中破坏输入。 相反,您应该编写一个函数,该函数采用ArrayBuffer并简单地递归,而不修改原始函数。

您需要eval函数来检查特定情况。 下面是一个简单的实现作为演示:

def eval(e: Seq[Any]): Int = 
  e match {
    case Seq("+", a: Int,      b: Int)      => a + b
    case Seq("+", a: Int,      b: Seq[Any]) => a + eval(b)
    case Seq("+", a: Seq[Any], b: Int)      => eval(a) + b
    case Seq("+", a: Seq[Any], b: Seq[Any]) => eval(a) + eval(b)
  }

所以你可以看到,对于(+ arg1 arg2)的简单情况,有4种情况。 在每种情况下,如果参数是 Int ,我们直接在加法中使用它。 如果参数本身是一个序列(like ArrayBuffer(,那么我们在添加之前递归计算。 另请注意,Scala 的case语法允许与类型进行模式匹配,因此您可以跳过isInstanceOfasInstanceOf内容。

现在肯定有你想要做的风格改进(比如使用Either而不是Any,而不是硬编码"+"(,但这应该让你走上正确的轨道。

以下是您将如何使用它:

eval(Seq("+", 3, 2))
res0: Int = 5
scala> eval(Seq("+", 4, Seq("+", 3, 2)))
res1: Int = 9

现在,如果你想真正利用 Scala 的特性,你可以使用 Eval 提取器

object Eval {
  def unapply(e: Any): Option[Int] = {
    e match {
      case i: Int => Some(i)
      case Seq("+", Eval(a), Eval(b)) => Some(a + b)
    }
  }
}

你会像这样使用它:

scala> val Eval(result) = 2
result: Int = 2
scala> val Eval(result) = ArrayBuffer("+", 2, 3)
result: Int = 5
scala> val Eval(result) = ArrayBuffer("+", 2, ArrayBuffer("+", 2, 3))
result: Int = 7

或者你可以把它包装在一个eval函数中:

def eval(e: Any): Int = {
  val Eval(result) = e
  result
}

以下是我对从右到左基于堆栈的评估的看法:

def eval(expr: String): Either[Throwable, Int] = {
  import java.lang.NumberFormatException
  import scala.util.control.Exception._
  def int(s: String) = catching(classOf[NumberFormatException]).opt(s.toInt)
  val symbols = expr.replaceAll("""[^d+-*/ ]""", "").split(" ").toSeq
  allCatch.either {
    val results = symbols.foldRight(List.empty[Int]) {
      (symbol, operands) => int(symbol) match {
        case Some(op) => op :: operands
        case None => val x :: y :: ops = operands
        val result = symbol match {
          case "+" => x + y
          case "-" => x - y
          case "*" => x * y
          case "/" => x / y
        }
        result :: ops
      }
    }
    results.head
  }
}

最新更新