【问题标题】:Calculate a mathematical expression stored in String - Scala计算存储在字符串中的数学表达式 - Scala
【发布时间】:2016-01-29 20:37:49
【问题描述】:

我有一个类似 val s = "5 - 3 + 2" 的字符串(可以有任意数量的加法/减法)。使用模式匹配我想计算这个表达式(所以这个例子的结果是 4)。 我的第一个想法是做这样的事情:

object calculator{
    val s = "5 - 3 + 2"
    val Pattern = "(\\d+)".r

    def main(args: Array[String]) {
       val matches = Pattern.findAllIn(s).toArray
       for (mymatch <- matches) mymatch.toInt
       var sum = 0
       matches.foreach(sum += _)
       println(sum)   
    }
}

它甚至没有假设我可以进行减法(我不知道该怎么做),但是有一个错误:类型不匹配;找到:String#3416175 需要:Int#1078

另一种方法是使用匹配器并以某种方式评估这些表达式:

val Pattern = ((?:\\d+\\s*[-+]\\s+)*\\d+).r
def matcher(expression: String): Any = expression match {
        case Pattern(expression) => expression //evaluate expression here?
        case _ => "wrong expression"
    }

任何想法如何实现这一目标?

【问题讨论】:

标签: regex scala pattern-matching


【解决方案1】:

如果您的表达式只包含加法和减法,您可以使用如下内容:

val expression = "5 - 3 + 2"

def evaluate(expression: List[String]): Int = expression match {
    case l :: "+" :: r :: rest => evaluate((l.toInt + r.toInt).toString :: rest)
    case l :: "-" :: r :: rest => evaluate((l.toInt - r.toInt).toString :: rest)
    case value :: Nil => value.toInt
}

val expressionElements = expression.split("\\s").toList

val result = evaluate(expressionElements)

println(s"$expression = $result")

您可以在此处 (http://lampwww.epfl.ch/~emir/written/MatchingObjectsWithPatterns-TR.pdf) 找到模式匹配如何工作的详细说明,但我会尝试逐步解释这个特定示例。

首先你必须了解如何构造 List。

val list = List(1, 2, 3) 
val list = 1 :: 2 :: 3 :: Nil
(two lists with 3 elements: 1, 2, 3)

所以我们的表达式是这样的:

  val expressionElements = expression.split("\\s").toList  
//val expressionElements = "5" :: "-" :: "3" :: "+" :: "2" :: Nil

evaluate函数支持三种情况:

  1. 添加 - case l :: "+" :: r :: rest =&gt;
  2. 减法 - case l :: "-" :: r :: rest =&gt;
  3. “价值”-case value :: Nil =&gt;

第一个 evaluate 调用,它是(尾)递归函数,将是这样的:

evaluate ("5" :: "-" :: "3" :: "+" :: "2" :: Nil) = "5" :: "-" :: "3" :: "+" :: "2" :: Nil match {
   case l   :: "-" :: r   :: rest              => evaluate (l - r :: rest)
 //case "5" :: "-" :: "3" :: "+" :: "2" :: Nil => evaluate (5 - 3 :: "+" :: "2" :: Nil)
 //case "5" :: "-" :: "3" :: "+" :: "2" :: Nil => evaluate ("2"   :: "+" :: "2" :: Nil)
}

第二次通话:

evaluate ("2" :: "+" :: "2" :: Nil) = "2" :: "+" :: "2" :: Nil match {
  case l   :: "+" :: r   :: rest  => evaluate (l + r :: rest)
//case "2" :: "+" :: "2" :: Nil   => evaluate (2 + 2 :: rest)
//case "2" :: "+" :: "2" :: Nil   => evaluate ("4"   :: Nil)
}

第三次调用:

evaluate ("4"   :: Nil) = "4"   :: Nil match {
  case value :: Nil => value.toInt
//case "4"   :: Nil => "4".toInt
}

所以表达式被评估为 4。

【讨论】:

  • 这很好,但你能解释一下匹配是如何工作的吗?我在弄清楚这一点时遇到了一些问题。
  • 答案中的详细解释。
【解决方案2】:

您是否有理由要使用正则表达式来执行此操作?还有其他解决方案...

您可以使用解析器组合器创建更强大的解决方案。看看 Don Roby 的 this blog post 就可以看出这一点。

您可以使用 Dijkstra 的两栈算法,例如 this stackoverflow,或者更好的是 from Princeton here

【讨论】:

【解决方案3】:
import scala.reflect.runtime.currentMirror
import scala.tools.reflect.ToolBox

val expr = "2*(2+3)"

val toolbox = currentMirror.mkToolBox()
val calc = toolbox.eval(toolbox.parse(expr))

结果是

calc: Any = 10

【讨论】:

  • 您好@Vasyl 我正在使用您上面的代码,但是 Power 运算符无法正常工作,请您告知原因。
猜你喜欢
  • 2015-12-03
  • 2011-06-04
  • 2018-05-19
  • 2016-05-04
相关资源
最近更新 更多