在scala中的嵌套列表上简洁collectFirst

时间:2018-10-10 15:30:12

标签: scala scala-collections

我正在Scala中编写一个简单的解释器。

此解释器存储本质上为List[List[(Symbol, Value)]]的环境:

case class Env(frames: List[Frame]) {
   def lookup(s: LSymbol): Option[Value] = ??? // help
}
case class Frame(associations: List[(LSymbol, Value)]){
 def find(s: LSymbol): Option[Value] = {
   this.associations.collectFirst {case (s1, v) if s1 == s => v}
 }
}

从本质上讲,我想依次搜索每个帧以找到匹配的符号。尽管为此编写一个小的尾部递归搜索功能非常简单,但感觉可以通过collectFirst单行代码更高效,更通用地完成某些事情,就像这样:

def lookup(s: LSymbol): Option[Value] = 
 this.frames.collectFirst{
  case frame if frame.find(s).nonEmpty => frame.find(s).get
 }

但是,这在第二个frame.find(s)中是多余的。有没有办法以一种不浪费的方式简洁地进行此查找?

2 个答案:

答案 0 :(得分:2)

使用view应该会使评估变得懒惰,然后可以像这样使用mapfilter

def lookup(s: LSymbol): Option[Value] =
  this.frames.view.map(_.find(s)).filter(_.nonEmpty).head

这是单线的,但可能不像您想的那么简洁...

答案 1 :(得分:2)

您可以定义一个专用提取器来提取frame.find(s)值。这样,您还将摆脱nonEmpty / get的调用,因为模式匹配已经为您做到了:

def lookup(s: LSymbol): Option[Value] = {
  object FindS {
    def unapply(frame: Frame): Option[Value] = frame.find(s)
  }
  this.frames.collectFirst {
    case FindS(value) => value
  }
}

如果您经常执行这样的操作,您甚至可能想要定义一个帮助器类以简化提取器的构造:

class Extractor[T, X](f: T => Option[X]) {
  def unapply(arg: T): Option[X] = f(arg)
}

def lookup(s: LSymbol): Option[Value] = {
  object FindS extends Extractor((frame: Frame) => frame.find(s))
  this.frames.collectFirst {
    case FindS(value) => value
  }
}