scala在body中使用模式匹配的guard(if)计算

0lvr5msh  于 2023-04-30  发布在  Scala
关注(0)|答案(3)|浏览(214)

我在scala中使用了很多模式匹配。很多时候我需要做一些计算,在警卫部分,有时他们是相当昂贵的。有没有办法将计算值绑定到单独的值?

//i wan't to use result of prettyExpensiveFunc in body safely
people.collect {
  case ...
  case Some(Right((x, y))) if prettyExpensiveFunc(x, y) > 0 => prettyExpensiveFunc(x)
}

//ideally something like that could be helpful, but it doesn't compile:
people.collect {
  case ...
  case Some(Right((x, y))) if {val z = prettyExpensiveFunc(x, y); y > 0} => z
}

//this solution works but it isn't safe for some `Seq` types and is risky when more cases are used.
var cache:Int = 0
people.collect {
  case ...
  case Some(Right((x, y))) if {cache = prettyExpensiveFunc(x, y); cache > 0} => cache
}

有没有更好的解决办法?ps:示例是简化的,我不期望答案表明我不需要模式匹配在这里。

czq61nw1

czq61nw11#

您可以使用cats.Eval使昂贵的计算变得懒惰和可记忆,使用.map创建Eval,并在.collect中提取.value(如果需要,最多计算一次

values.map { value =>
  val expensiveCheck1 = Eval.later { prettyExpensiveFunc(value) }
  val expensiveCheck2 = Eval.later { anotherExpensiveFunc(value) }
  (value, expensiveCheck1, expensiveCheck2)
}.collect {
  case (value, lazyResult1, _) if lazyResult1.value > 0 => ...
  case (value, _, lazyResult2) if lazyResult2.value > 0 => ...
  case (value, lazyResult1, lazyResult2) if lazyResult1.value > lazyResult2.value => ...
  ...
}

如果不创建一些惰性评估的实现,我看不到一种方法来做你想做的事情,如果你必须使用一个,你最好使用现有的一个,而不是自己滚动一个。
如果你还没有注意到的话,编辑一下,你并没有因为在这里使用元组而失去模式匹配的能力:

values.map {
  // originial value -> lazily evaluated memoized expensive calculation
  case a @ Some(Right((x, y)) => a -> Some(Eval.later(prettyExpensiveFunc(x, y)))
  case a                      => a -> None
}.collect {
  // match type and calculation
  ...
  case (Some(Right((x, y))), Some(lazyResult)) if lazyResult.value > 0 => ...
  ...
}
amrnrhlw

amrnrhlw2#

为什么不先对每个元素运行这个函数,然后再处理元组呢?

Seq(1,2,3,4,5).map(e => (e, prettyExpensiveFunc(e))).collect {
  case ...
  case (x, y) if y => y
}
qoefvg9y

qoefvg9y3#

我尝试了自己的匹配器和效果是某种程度上可以,但不完美。我的matcher是untyped的,要使它完全类型化有点难看。

class Matcher[T,E](f:PartialFunction[T, E]) {
  def unapply(z: T): Option[E] = if (f.isDefinedAt(z)) Some(f(z)) else None
}

def newMatcherAny[E](f:PartialFunction[Any, E]) = new Matcher(f)
def newMatcher[T,E](f:PartialFunction[T, E]) = new Matcher(f)

def prettyExpensiveFunc(x:Int) = {println(s"-- prettyExpensiveFunc($x)"); x%2+x*x}

val x = Seq(
  Some(Right(22)),
  Some(Right(10)), 
  Some(Left("Oh now")), 
  None
)
val PersonAgeRank = newMatcherAny { case Some(Right(x:Int)) => (x, prettyExpensiveFunc(x)) }

x.collect { 
  case PersonAgeRank(age, rank) if rank > 100 => println("age:"+age + " rank:" + rank) 
}

https://scalafiddle.io/sf/hFbcAqH/3

相关问题