我是 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)
的意思是"用一个元素构建一个ArrayBuffer
:2
"。 代码中没有任何地方引用参数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
语法允许与类型进行模式匹配,因此您可以跳过isInstanceOf
并asInstanceOf
内容。
现在肯定有你想要做的风格改进(比如使用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
}
}