【问题标题】:Collect results of multiple partial functions at single value?以单个值收集多个偏函数的结果?
【发布时间】:2015-08-23 09:34:30
【问题描述】:

假设我有一些可能具有重叠域的部分函数:

val funcs: Seq[PartialFunction[Any, Int]] = Vector(
{ case i: Int if i % 2 == 0 => i*2 }
,
{ case i: Int if i % 2 == 1 => i*3 }
,
{ case i: Int if i % 6 == 0 => i*5 }
)

我想将它们应用于一个值并收集在该 val 处定义的任何结果。我认为会有一种优雅的方式来使用collect,但我能做的最好的是

val num = 66
funcs.collect { case func if func.isDefinedAt(num) => func(num) }
// or 
funcs.filter(_.isDefinedAt(num)).map(_(num))

这看起来不太干净。有没有办法使用内置的案例机制来测试定义性并同时进行评估,也许是通过将它们与测试值或类似的东西相结合?

【问题讨论】:

  • 两个 PF 重叠时应该是什么行为?例如。您对f(8) 有何期待?
  • @maasg 我认为没有必要合并 PF。这些只是单独的 PF,当它们被定义为特定值时应该调用它们,并且单独的结果应该聚合在一个列表中。 (另外附注8 不是重叠的值,但66 是,结果将是:132, 130
  • 我想要两个值——这就是为什么我将它定义为一个部分函数列表,而不是一个具有多个 cases 的单个函数(据我所知,这是不可能的从第一个以外的任何匹配案例中获取结果)

标签: scala pattern-matching partialfunction


【解决方案1】:

部分函数有一个lift 方法,如果定义了PF,则为Some(res),如果未定义,则为None。同样使用flatMap,我们基本上可以忽略Nones,只获取Some的值。

funcs.flatMap { func => func.lift(num) }

如果你喜欢这种东西,也可以压缩:

funcs.flatMap(_.lift(num))

【讨论】:

  • 完美,这正是我所需要的
【解决方案2】:

同时测试定义性和评估

那是applyOrElsecollectlift 使用它来避免重复评估。

这个递归版本说,应用下一个函数并使用它来构建结果,或者只是继续并构建其余的结果。

def f(ff: List[PartialFunction[Any, Int]]): List[Int] = ff match {
  case hd :: tail => hd.applyOrElse(num, (_: Any) => return f(tail)) :: f(tail)
  case _ => Nil
}

这是我第一次在 Scala 中使用 return。我必须在索引中查找它。

这种手动构造避免了在LiftedOption 中创建一些对象并适应Option

scala> :pa
// Entering paste mode (ctrl-D to finish)

val funcs: Seq[PartialFunction[Any, Int]] = Vector(
{ case i: Int if i % 2 == 0 => i*2 }
,
{ case i: Int if i % 2 == 1 => i*3 }
,
{ case i: Int if i % 6 == 0 => i*5 }
)
val num = 66

def f(ff: List[PartialFunction[Any, Int]]): List[Int] = ff match {
  case hd :: tail => hd.applyOrElse(num, (_: Any) => return f(tail)) :: f(tail)
  case _ => Nil
}

// Exiting paste mode, now interpreting.

funcs: Seq[PartialFunction[Any,Int]] = Vector(<function1>, <function1>, <function1>)
num: Int = 66
f: (ff: List[PartialFunction[Any,Int]])List[Int]

scala> f(funcs.toList)
res0: List[Int] = List(132, 330)

scala> funcs flatMap (_ lift num)
res1: Seq[Int] = Vector(132, 330)

第二个版本只是为相同的想法使用了一个标志。

scala> :pa
// Entering paste mode (ctrl-D to finish)

def f(ff: List[PartialFunction[Any, Int]]): List[Int] = ff match {
  case hd :: tail =>
    var ok = true
    val x  = hd.applyOrElse(num, (_: Any) => { ok = false ; 0 })
    if (ok) x :: f(tail) else f(tail)
  case _ => Nil
}

// Exiting paste mode, now interpreting.

f: (ff: List[PartialFunction[Any,Int]])List[Int]

scala> f(funcs.toList)
res2: List[Int] = List(132, 330)

【讨论】:

    猜你喜欢
    • 2010-10-19
    • 1970-01-01
    • 2017-02-22
    • 1970-01-01
    • 2021-10-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-04-12
    相关资源
    最近更新 更多