【问题标题】:Keeping intermediate results of function composition保留函数组合的中间结果
【发布时间】:2016-02-09 03:33:55
【问题描述】:

假设我有以下三个函数:

 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

现在假设我需要保留执行的中间结果f1f2f3,并将它们传递给调用者:

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)

这有意义吗?您将如何改进/简化此解决方案?

【问题讨论】:

  • 您是否也需要失败情况下的中间结果,或者仅当整个链成功时?
  • 我主要需要失败的中间结果。

标签: scala function composition


【解决方案1】:

同构列表

假设单一类型非常简单

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)

将会

列表(一些(4),一些(2),一些(7),一些(3))

保留所有中间值。

异构列表

但是如果涉及到多种类型我们可以吗?

幸运的是,有一个名为 Heterogenous List 的特殊结构的 shapeless 库可以处理类似列表的多类型值序列。

假设我们有

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

类转换器

现在,如果您敦促将结果保存在某个类中,例如

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))

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-07-25
    • 2019-07-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-06-14
    • 1970-01-01
    相关资源
    最近更新 更多