保留函数组合的中间结果
Keeping intermediate results of function composition
假设我有以下三个函数:
val f1: Int => Option[String] = ???
val f2: String => Option[Int] = ???
val f3: Int => Option[Int] = ???
我可以这样组合它们:
val f: Int => Option[Int] = x =>
for {
x1 <- f1(x)
x2 <- f2(x1)
x3 <- f3(x2)
} yield x3
假设现在我需要保留执行的中间结果f1
、f2
、f3
并将它们传递给调用者:
class Result(x: Int) {
val r1 = f1(x)
val r2 = r1 flatMap f2
val r3 = r2 flatMap f3
def apply = r3
}
val f: Int => Result = x => new Result(x)
有意义吗?您 improve/simplify 如何解决这个问题?
同类列表
单一类型很简单,假设
val g1: Int => Option[Int] = x => if (x % 2 == 1) None else Some(x / 2)
val g2: Int => Option[Int] = x => Some(x * 3 + 1)
val g3: Int => Option[Int] = x => if (x >= 4) Some(x - 4) else None
你可以定义
def bind[T]: (Option[T], T => Option[T]) => Option[T] = _ flatMap _
def chain[T](x: T, fs: List[T => Option[T]]) = fs.scanLeft(Some(x): Option[T])(bind)
现在
chain(4, g1 :: g2 :: g3 :: Nil)
将会
List(Some(4), Some(2), Some(7), Some(3))
保留所有中间值。
异构列表
但是涉及到多个类型的时候可以吗?
幸运的是 shapeless library for special structures named Heterogenous List 可以处理类似列表的多类型值序列。
所以假设我们有
import scala.util.Try
val f1: Int => Option[String] = x => Some(x.toString)
val f2: String => Option[Int] = x => Try(x.toInt).toOption
val f3: Int => Option[Int] = x => if (x % 2 == 1) None else Some(x / 2)
让我们定义以前函数的异构类比:
import shapeless._
import ops.hlist.LeftScanner._
import shapeless.ops.hlist._
object hBind extends Poly2 {
implicit def bind[T, G] = at[T => Option[G], Option[T]]((f, o) => o flatMap f)
}
def hChain[Z, L <: HList](z: Z, fs: L)
(implicit lScan: LeftScanner[L, Option[Z], hBind.type]) =
lScan(fs, Some(z))
现在
hChain(4, f1 :: f2 :: f3 :: HNil)
评估为
Some(4) :: Some("4") :: Some(4) :: Some(2) :: HNil
Class转换器
现在,如果您敦促将结果保存在 class 中,例如
case class Result(init: Option[Int],
x1: Option[String],
x2: Option[Int],
x3: Option[Int])
您可以轻松使用它 Generic
representation
只要确保自己
Generic[Result].from(hChain(4, f1 :: f2 :: f3 :: HNil)) ==
Result(Some(4),Some("4"),Some(4),Some(2))
假设我有以下三个函数:
val f1: Int => Option[String] = ???
val f2: String => Option[Int] = ???
val f3: Int => Option[Int] = ???
我可以这样组合它们:
val f: Int => Option[Int] = x =>
for {
x1 <- f1(x)
x2 <- f2(x1)
x3 <- f3(x2)
} yield x3
假设现在我需要保留执行的中间结果f1
、f2
、f3
并将它们传递给调用者:
class Result(x: Int) {
val r1 = f1(x)
val r2 = r1 flatMap f2
val r3 = r2 flatMap f3
def apply = r3
}
val f: Int => Result = x => new Result(x)
有意义吗?您 improve/simplify 如何解决这个问题?
同类列表
单一类型很简单,假设
val g1: Int => Option[Int] = x => if (x % 2 == 1) None else Some(x / 2)
val g2: Int => Option[Int] = x => Some(x * 3 + 1)
val g3: Int => Option[Int] = x => if (x >= 4) Some(x - 4) else None
你可以定义
def bind[T]: (Option[T], T => Option[T]) => Option[T] = _ flatMap _
def chain[T](x: T, fs: List[T => Option[T]]) = fs.scanLeft(Some(x): Option[T])(bind)
现在
chain(4, g1 :: g2 :: g3 :: Nil)
将会
List(Some(4), Some(2), Some(7), Some(3))
保留所有中间值。
异构列表
但是涉及到多个类型的时候可以吗?
幸运的是 shapeless library for special structures named Heterogenous List 可以处理类似列表的多类型值序列。
所以假设我们有
import scala.util.Try
val f1: Int => Option[String] = x => Some(x.toString)
val f2: String => Option[Int] = x => Try(x.toInt).toOption
val f3: Int => Option[Int] = x => if (x % 2 == 1) None else Some(x / 2)
让我们定义以前函数的异构类比:
import shapeless._
import ops.hlist.LeftScanner._
import shapeless.ops.hlist._
object hBind extends Poly2 {
implicit def bind[T, G] = at[T => Option[G], Option[T]]((f, o) => o flatMap f)
}
def hChain[Z, L <: HList](z: Z, fs: L)
(implicit lScan: LeftScanner[L, Option[Z], hBind.type]) =
lScan(fs, Some(z))
现在
hChain(4, f1 :: f2 :: f3 :: HNil)
评估为
Some(4) :: Some("4") :: Some(4) :: Some(2) :: HNil
Class转换器
现在,如果您敦促将结果保存在 class 中,例如
case class Result(init: Option[Int],
x1: Option[String],
x2: Option[Int],
x3: Option[Int])
您可以轻松使用它 Generic
representation
只要确保自己
Generic[Result].from(hChain(4, f1 :: f2 :: f3 :: HNil)) ==
Result(Some(4),Some("4"),Some(4),Some(2))