波兰符号评估功能

时间:2012-03-21 03:03:17

标签: scala

我是Scala的新手,我很难定义,或者更有可能将我的代码从Ruby转换为评估描述为波兰语符号的计算。 f.e(+ 3 2)或( - 4(+ 3 2))

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

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

在基础案例中: 我想简单地检查第二个元素是否是一个instanceOf [Int]&&第3个元素是instanceOf [Int]然后一起评估它们(取决于符号运算符 - 第1个元素)并返回int。

但是如果任何元素是另一个ArrayBuffer,我只想将该元素重新分配给递归调用的eval函数的返回值。喜欢: 存储(2)= eval(存储(2)。(**这就是为什么我使用可变的ArrayBuffer **)

我得到的错误是: scala.collection.mutable.ArrayBuffer无法强制转换为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]
  }
}

2 个答案:

答案 0 :(得分:5)

您的代码存在一些问题:

  • 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
}

答案 1 :(得分:0)

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

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
  }
}