Scala:如何使用类型集合?

标签 scala

我正在为一种小语言手工编写一个递归下降解析器。在我的词法分析器中,我有:

trait Token{def position:Int}
trait Keyword  extends Token
trait Operator extends Token

case class Identifier(position:Int, txt:String) extends Token
case class If        (position:Int)             extends Keyword
case class Plus      (position:Int)             extends Operator
/* etcetera; one case class per token type */

我的解析器运行良好,现在我想合并一些错误恢复:替换、插入或丢弃标记,直到某个同步点。

为此,如果有一个在无效的 Scala 中看起来像这样的函数会很方便

def scanFor(tokenSet:Set[TokenClass], lookahead:Int) = {
  lexer.upcomingTokens.take(lookahead).find{ token =>
    tokenSet.exists(tokenClass => token.isInstanceOf[tokenClass])
  }
}

我会调用它,例如:scanFor(Set(Plus, Minus, Times, DividedBy), 4)

但是TokenClass当然不是一个有效的类型,而且我不知道如何创建之前的集合。

作为替代方案:

  1. 我可以创建一个新特征,并使我想要检查的 token 集中的所有 token 类扩展该特征,然后针对该特征执行 instanceOf 检查。但是,我可能有几个这样的集合,这可能会使它们难以命名,并且代码以后难以维护。
  2. 我可以创建 isXXX:Token=>Boolean 函数,并创建这些函数的集合,但这看起来不太优雅

有什么建议吗?

最佳答案

如果这样的组合只有少数,我实际上建议使用额外的特征。它很容易编写和理解,并且运行时速度很快。说起来也不算太糟糕

case class Plus(position: Int)
extends Operator with Arithmetic with Precedence7 with Unary

但是还有很多替代方案。

如果您不介意繁琐的手动维护过程并且需要真正快速的东西,那么为每个 token 类型定义一个 ID 号(您必须手动保持不同)将允许您使用 Set[Int]BitSet 甚至只是一个 Long 来选择您喜欢的类。然后,您可以执行集合操作(​​并集、交集)来相互构建这些选择器。编写单元测试来帮助使挑剔的部分变得更加可靠并不难。如果您至少可以列出所有类型:

val everyone = Seq(Plus, Times, If /* etc */)
assert(everyone.length == everyone.map(_.id).toSet.size)

因此,如果您认为性能和可组合性至关重要,那么您不必对这种方法过于 panic 。

您还可以编写自定义提取器,它可以(更慢地)通过模式匹配提取正确的标记子集。例如,

object ArithOp {
  def unapply(t: Token): Option[Operator] = t match {
    case o: Operator => o match {
      case _: Plus | _: Minus | _: Times | _: DividedBy => Some(o)
      case _ => None
    }
    case _ => None
  }
}
如果操作类型不正确,

将为您提供None。 (在本例中,我假设除了 Operator 之外没有父级。)

最后,您可能可以将类型表示为联合和 HList,并使用 Shapeless 以这种方式挑选它们,但我个人没有使用解析器执行此操作的经验,因此我不确定您可能会遇到什么困难.

关于Scala:如何使用类型集合?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34980900/

相关文章:

scala - 在没有继承层次结构的scala中编写通用代码

unit-testing - 斯卡拉模拟。模拟一个接受参数的类

json - Play Framework WS : Returning JSON data as Int

scala - 使用 Dispatch 执行简单的 HTTP GET

scala - ( Play 2.4)特征中的依赖注入(inject)?

mysql - 返回两种类型的 Slick TableQuerys 之一的 Scala 函数

scala - 如何在 RxScala/Java 中使用多线程执行 map、filter、flatMap?

java - Spark-Submit 作业日志跟踪

json - Scala 中的 JsOnobject 和 JsValue 有什么区别?

scala - 尽管是包装器,但 CollectionConverters 的性能代价高昂?