偏函数
偏函数最基本的定义是,我们想给函数固定定义域。在 scala 中,我们使用PartialFunction 来实现,定义如下:
object TestFuture {
def main(args: Array[String]): Unit = {
val divide = new PartialFunction[Int, Int] {
def isDefinedAt(x: Int): Boolean = x != 0
def apply(x: Int): Int = 100/x
}
println(divide(10))
if (divide.isDefinedAt(0)) {
println(divide(0))
} else {
println("can not divided by zero")
}
}
}
输出结果:
10
can not divided by zero
但是这类方式过于繁琐了,我们可以结合 case的方式来简化定义,比如:
object TestPartial {
def main(args: Array[String]): Unit = {
val divide: PartialFunction[Int, Int] = {
case x if x != 0 => 100/x
}
divide(10)
}
}
上面的定义在除数为0的时候,仍然会抛出异常,我们可以定义下异常情况,然后组合到一起,借助orElse 来组合。给出代码示例:
object TestPartial {
def main(args: Array[String]): Unit = {
// 偏函数必须声明类型
val divideNonZero: PartialFunction[Int, Int] = {
case x if x != 0 => 100/x
}
val divideZero: PartialFunction[Int, Int] = {
case 0 => -1
}
// 组合后仍是偏函数
val divide = divideNonZero orElse divideZero
println(divide(10)) // 10
println(divide(0)) // -1
}
}
上面的定义方式,其实就是组合了一个match 块,等加成下面的定义方式:
def div(x: Int): Int = {
x match {
case 0 => -1
case _ => 100/x
}
}
只不过偏函数的方式更加函数化了一些。
除了orElse之外,我们还有andThen 方式,顾名思义,衔接前后的结果。举个例子:
object TestPartial {
def main(args: Array[String]): Unit = {
val add1: PartialFunction[Int, Int] = {
case x => x + 1
}
val add2: PartialFunction[Int, Int] = {
case x => x + 2
}
val add = add1 andThen add2
println(add(3)) // 6
}
}
可以看出,andThen 按照链式的方式衔接前后的结果。
柯理化
我们先看一下函数柯理化的定义:zh.wikipedia.org/wiki/柯里化
scala 的柯理化是指,把接受两个参数函数,固定一个参数后,返回一个新函数的行为。给出代码示例:
object TestCurry {
def main(args: Array[String]): Unit = {
def multiply(a: Int)(b: Int) = a * b
def mul3 = multiply(3)_ // 注意这里的下划线
println(mul3(10))
}
}
柯理化之后,我们可以省去每次传入的固定参数了,抽象层级也更高了。
隐式参数
先给个例子:
object TestImplicit {
def main(args: Array[String]): Unit = {
val value = 10
implicit val multiplier: Int = 3
def multiply(implicit by: Int) = value * by
println(multiply) // 30
println(multiply(10)) // 100
}
}
函数在声明隐式参数implicit 之后,可以不去显示的指定参数。如果不指定参数,则会先找当前代码块中声明为implicit 同类型的数据进行填充;如果当前代码块不存在,则继续向上找;知道最后找不到的话,报错。
隐式参数结合柯理化用更加广泛的用途,尤其是在各类基础代码库中。我们给一个简单的例子,说明如何结合的:
object TestImplicitCurry {
def main(args: Array[String]): Unit = {
implicit val op = "add"
def numOp(a: Int, b: Int)(implicit op: String): Int = {
op match {
case "add" => a + b
case _ => 0
}
}
val result = numOp(1, 2)
println(result) // 3
}
numOp 会自动使用op 参数。
隐式对象
scala 的class 也可以声明为implicit 的。implicit 可以对传入的class进行包装。被包转的类可以直接调用implict包转类的方法。举个例子说明:
object TestFuture {
def main(args: Array[String]): Unit = {
val t = 5
t times println("HI")
}
implicit class IntWithTimes(x: Int) {
def times[A](f: => A): Unit = {
var cur = x;
while (cur > 0) {
f
Thread.sleep(1000)
cur -= 1
}
}
}
}
main 函数的Int类型的t没有times方法,但是scala直接把t隐式转换成了IntWithTimes,此时就有了对应的方法了。
implicit class 必须定义在object class 或者trait 内部,而且必须要有方法,同时不能是case class。可以使用一个隐式参数