函数组合,累积中间结果
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
现在我想将它们和组合成一个新函数,它累积中间结果,即f1
、f2
的结果,和 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。你能建议另一种方法来编写一个由 f1
、f2
和 f3
组成的函数 f
并且 return 也是中间结果,即 [ 的结果=14=]、f2
和 f3
调用。
为什么不使用带有函数列表的 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))
您可以在 Function
的 List
上使用 .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
)。
假设我有几个函数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
现在我想将它们和组合成一个新函数,它累积中间结果,即f1
、f2
的结果,和 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。你能建议另一种方法来编写一个由 f1
、f2
和 f3
组成的函数 f
并且 return 也是中间结果,即 [ 的结果=14=]、f2
和 f3
调用。
为什么不使用带有函数列表的 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))
您可以在 Function
的 List
上使用 .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
)。