Scala 函数组合 totalFn(partialFn(totalFn(x)))
Scala function composition totalFn(partialFn(totalFn(x)))
我试图组合三个函数,只有中间一个是 PartialFunction。我希望结果类型也是 PartialFunction。
示例:
val mod10: Int => Int = _ % 10
val inverse: PartialFunction[Int, Double] = { case n if n != 0 => 1.0 / n }
val triple: Double => Double = _ * 3
val calc: Int => Double = mod10 andThen inverse andThen triple
但是,calc
并未在其整个域上定义。它会为每个可被 10 整除的数字抛出 MatchError。
当组合中至少有一个函数是部分函数时返回总函数的原因是什么?
另一个示例,其中部分函数的组合导致另一个部分函数具有不正确的域条件:
val inverse: PartialFunction[Double, Double] = { case n if n != 0 => 1.0 / n }
val arcSin: PartialFunction[Double, Double] = {
case n if math.abs(n) <= 1 => math.asin(n)
}
val calc: PartialFunction[Double, Double] = inverse andThen arcSin
我希望 calc
的域是 (-Infinity, -1] union [1, Infinity)
但是调用 calc.lift(0.5)
会抛出 MathError 而不是返回 None
因为输入在第一个函数的范围内域名。
谢谢,
诺伯特
我认为错误是您唯一期望的非零值。
{ case n if n != 0 => 1.0 / n }
那么如果它等于零怎么办,这就是匹配错误的原因..
{
case n if n != 0 => 1.0 / n // non-zero value.
case n if n == 0 => // zero value.
}
希望对您有所帮助。
Example 1: What is the reason for returning a total function when at least one of the functions in the composition is partial?
这是因为第一个示例中的第一个函数是总函数 (Function1) 并且其 andThen 方法 return无论第二个函数是总函数还是部分函数都是 Function1:
def andThen[A](g: (R) => A): (T1) => A
我的猜测是,Scala 语言设计团队更喜欢 returned 值,因为 PartialFunction is a subclass of Function 并且宁愿让用户根据需要派生专用代码。
Example 2: calling calc.lift(0.5) will throw a MathError instead of returning None
从 PartialFunction API doc 开始,通过 andThen
组合两个偏函数将 return 一个与第一个偏函数具有相同定义域的偏函数:
def andThen[C](k: (B) => C): PartialFunction[A, C]
因此,合成函数忽略了 inverse(0.5)
(即 2.0)在第二个偏函数 arcSin
.
域之外的事实
那么,当使用 andThen
将一个函数(全部或部分)与部分函数组合时,我们如何才能使它 return 成为具有适当域的部分函数?
类似于此 SO Q&A 中演示的内容,可以通过几个隐式 类 来增强 andThen
以将合成函数的域限制为第一个函数的子集return 值在部分函数域内的函数域:
object ComposeFcnOps {
implicit class TotalCompose[A, B](f: Function[A, B]) {
def andThenPartial[C](that: PartialFunction[B, C]): PartialFunction[A, C] =
Function.unlift(x => Option(f(x)).flatMap(that.lift))
}
implicit class PartialCompose[A, B](pf: PartialFunction[A, B]) {
def andThenPartial[C](that: PartialFunction[B, C]): PartialFunction[A, C] =
Function.unlift(x => pf.lift(x).flatMap(that.lift))
}
}
使用示例函数进行测试:
import ComposeFcnOps._
val mod10: Int => Int = _ % 10
val inverse1: PartialFunction[Int, Double] = { case n if n != 0 => 1.0 / n }
val triple: Double => Double = _ * 3
val calc1 = mod10 andThenPartial inverse1 andThen triple
// calc1: PartialFunction[Int,Double] = <function1>
calc1.isDefinedAt(0)
// res1: Boolean = false
val inverse2: PartialFunction[Double, Double] = { case n if n != 0 => 1.0 / n }
val arcSin: PartialFunction[Double, Double] = {
case n if math.abs(n) <= 1 => math.asin(n)
}
val calc2 = inverse2 andThenPartial arcSin
// calc2: PartialFunction[Double,Double] = <function1>
calc2.isDefinedAt(0.5)
// res2: Boolean = false
calc2.lift(0.5)
// res3: Option[Double] = None
andThen
是在 Function1
上定义的,根本不是为了组合部分函数而设计的。因此,我建议在使用之前将它们提升到总功能。
val calc = Function.unlift(mod10 andThen inverse.lift andThen (_.map(triple)))
和
val calc = Function.unlift(inverse.lift andThen (_.flatMap(arcSin.lift)))
我试图组合三个函数,只有中间一个是 PartialFunction。我希望结果类型也是 PartialFunction。
示例:
val mod10: Int => Int = _ % 10
val inverse: PartialFunction[Int, Double] = { case n if n != 0 => 1.0 / n }
val triple: Double => Double = _ * 3
val calc: Int => Double = mod10 andThen inverse andThen triple
但是,calc
并未在其整个域上定义。它会为每个可被 10 整除的数字抛出 MatchError。
当组合中至少有一个函数是部分函数时返回总函数的原因是什么?
另一个示例,其中部分函数的组合导致另一个部分函数具有不正确的域条件:
val inverse: PartialFunction[Double, Double] = { case n if n != 0 => 1.0 / n }
val arcSin: PartialFunction[Double, Double] = {
case n if math.abs(n) <= 1 => math.asin(n)
}
val calc: PartialFunction[Double, Double] = inverse andThen arcSin
我希望 calc
的域是 (-Infinity, -1] union [1, Infinity)
但是调用 calc.lift(0.5)
会抛出 MathError 而不是返回 None
因为输入在第一个函数的范围内域名。
谢谢, 诺伯特
我认为错误是您唯一期望的非零值。
{ case n if n != 0 => 1.0 / n }
那么如果它等于零怎么办,这就是匹配错误的原因..
{
case n if n != 0 => 1.0 / n // non-zero value.
case n if n == 0 => // zero value.
}
希望对您有所帮助。
Example 1: What is the reason for returning a total function when at least one of the functions in the composition is partial?
这是因为第一个示例中的第一个函数是总函数 (Function1) 并且其 andThen 方法 return无论第二个函数是总函数还是部分函数都是 Function1:
def andThen[A](g: (R) => A): (T1) => A
我的猜测是,Scala 语言设计团队更喜欢 returned 值,因为 PartialFunction is a subclass of Function 并且宁愿让用户根据需要派生专用代码。
Example 2: calling calc.lift(0.5) will throw a MathError instead of returning None
从 PartialFunction API doc 开始,通过 andThen
组合两个偏函数将 return 一个与第一个偏函数具有相同定义域的偏函数:
def andThen[C](k: (B) => C): PartialFunction[A, C]
因此,合成函数忽略了 inverse(0.5)
(即 2.0)在第二个偏函数 arcSin
.
那么,当使用 andThen
将一个函数(全部或部分)与部分函数组合时,我们如何才能使它 return 成为具有适当域的部分函数?
类似于此 SO Q&A 中演示的内容,可以通过几个隐式 类 来增强 andThen
以将合成函数的域限制为第一个函数的子集return 值在部分函数域内的函数域:
object ComposeFcnOps {
implicit class TotalCompose[A, B](f: Function[A, B]) {
def andThenPartial[C](that: PartialFunction[B, C]): PartialFunction[A, C] =
Function.unlift(x => Option(f(x)).flatMap(that.lift))
}
implicit class PartialCompose[A, B](pf: PartialFunction[A, B]) {
def andThenPartial[C](that: PartialFunction[B, C]): PartialFunction[A, C] =
Function.unlift(x => pf.lift(x).flatMap(that.lift))
}
}
使用示例函数进行测试:
import ComposeFcnOps._
val mod10: Int => Int = _ % 10
val inverse1: PartialFunction[Int, Double] = { case n if n != 0 => 1.0 / n }
val triple: Double => Double = _ * 3
val calc1 = mod10 andThenPartial inverse1 andThen triple
// calc1: PartialFunction[Int,Double] = <function1>
calc1.isDefinedAt(0)
// res1: Boolean = false
val inverse2: PartialFunction[Double, Double] = { case n if n != 0 => 1.0 / n }
val arcSin: PartialFunction[Double, Double] = {
case n if math.abs(n) <= 1 => math.asin(n)
}
val calc2 = inverse2 andThenPartial arcSin
// calc2: PartialFunction[Double,Double] = <function1>
calc2.isDefinedAt(0.5)
// res2: Boolean = false
calc2.lift(0.5)
// res3: Option[Double] = None
andThen
是在 Function1
上定义的,根本不是为了组合部分函数而设计的。因此,我建议在使用之前将它们提升到总功能。
val calc = Function.unlift(mod10 andThen inverse.lift andThen (_.map(triple)))
和
val calc = Function.unlift(inverse.lift andThen (_.flatMap(arcSin.lift)))