函数组合,累积中间结果

Functions composition, which accumulates intermediate results

假设我有几个函数Int => Option[Int]:

val f1: Int => Option[Int] = x => if (x < 10) Some(x + 1) else None
val f2: Int => Option[Int] = x => if (x < 10) Some(x + 2) else None
val f3: Int => Option[Int] = x => if (x < 10) Some(x + 3) else None

现在我想将它们组合成一个新函数,它累积中间结果,即f1f2的结果,和 f3

所以我添加了一个新的 class Accumulator:

class Accumulator(x: Int) {
  val ox1 = f1(x)
  val ox2 = ox1.flatMap(f2)
  val ox3 = ox2.flatMap(f3)
  def apply() = ox3
}

val f = {x => new Accumulator(x)}

现在我可以看到计算的所有中间结果:

scala> f(0)
res18: X = $$$a5cddfc4633c5dd8aa603ddc4f9aad5$$$$w$X@10596df6

scala> res18.ox1
res19: Option[Int] = Some(1)

scala> res18.ox2
res20: Option[Int] = Some(3)

scala> res18()
res21: Option[Int] = Some(6)

我不喜欢这种方法,因为每次计算都需要一个新的 class。你能建议另一种方法来编写一个由 f1f2f3 组成的函数 f 并且 return 也是中间结果,即 [ 的结果=14=]、f2f3 调用。

为什么不使用带有函数列表的 foldLeft

def accumulate(x: Int, funcs: List[Int => Option[Int]]): List[Option[Int]] = funcs.foldLeft(List[Option[Int]]()) {
  case (Nil, func) => List(func(x))
  case (res :: tail, func) => res.flatMap(func) :: res :: tail
}.reverse

val f1: Int => Option[Int] = x => if (x < 10) Some(x + 1) else None
val f2: Int => Option[Int] = x => if (x < 10) Some(x + 2) else None
val f3: Int => Option[Int] = x => if (x < 10) Some(x + 3) else None

accumulate(0, List(f1, f2, f3))

这给出 List[Option[Int]] = List(Some(1), Some(3), Some(6))

编辑:

正如 Marth 指出的那样,有一个专门用于此的功能 - scanLeft,但是,我想提出一种不同的使用方法。将初始值设为输入参数而不是函数:

def accumulate(x: Int, funcs: List[Int => Option[Int]]): List[Option[Int]] =
  funcs.scanLeft(Option(x)) {
    case (acc, op) => acc.flatMap(op)
  }.tail

val f1: Int => Option[Int] = x => if (x < 10) Some(x + 1) else None
val f2: Int => Option[Int] = x => if (x < 10) Some(x + 2) else None
val f3: Int => Option[Int] = x => if (x < 10) Some(x + 3) else None

accumulate(0, List(f1, f2, f3))

您可以在 FunctionList 上使用 .scanLeft,其中(来自文档):

Produces a collection containing cumulative results of applying the operator going left to right.

scala> val f1: Int => Option[Int] = x => if (x < 10) Some(x + 1) else None
f1: Int => Option[Int] = <function1>

scala> val f2: Int => Option[Int] = x => if (x < 10) Some(x + 2) else None
f2: Int => Option[Int] = <function1>

scala> val f3: Int => Option[Int] = x => if (x < 10) Some(x + 3) else None
f3: Int => Option[Int] = <function1>

scala> val fList = List(f1,f2,f3)
fList: List[Int => Option[Int]] = List(<function1>, <function1>, <function1>)

scala> val composed = fList.scanLeft((x:Int) => Option(x)) {
         case (composedFun, f) => (x:Int) => (composedFun(x)) flatMap f 
       }.tail
composedFunctions: List[Int => Option[Int]] = List(<function1>, <function1>, <function1>)

scala> composed.map(_(2))
res24: List[Option[Int]] = List(Some(3), Some(5), Some(8))

scala> composed.map(_(8))
res25: List[Option[Int]] = List(Some(9), Some(11), None)

请注意,我必须引入一个初始值(z,此处 (x:Int) => Option(x))。
您可能想要编写一个函数,该函数采用函数列表并使用 funList.head 作为初始值(并在 funList.tail 而不是 funList 上调用 .scanLeft)。