课程目标
scala 混合了面向对象和函数式的特性,在函数式编程语言中,函数是“头等公民”,它和Int、String、Class等其他类型处于同等的地位,可以像其他类型的变量一样被传递和操作。
高阶函数包含
在scala中,函数就像和数字、字符串一样,可以将函数传递给一个方法。我们可以对算法进行封装,然后将具体的动作传递给方法,这种特性很有用。
我们之前学习过List的map方法,它就可以接收一个函数,完成List的转换。
示例
示例说明
将一个整数列表中的每个元素转换为对应个数的小星星
List(1, 2, 3...) => *, **, ***
步骤
参考代码
val func: Int => String = (num:Int) => "*" * num println((1 to 10).map(func))
定义
上面的代码,给(num:Int) => “*” * num函数赋值给了一个变量,但是这种写法有一些啰嗦。在scala中,可以不需要给函数赋值给变量,没有赋值给变量的函数就是匿名函数
val list = List(1, 2, 3, 4) // 字符串*方法,表示生成指定数量的字符串 val func_num2star = (num:Int) => "*" * num print(list.map(func_num2star))
示例
使用匿名函数优化上述代码
参考代码
println((1 to 10).map(num => "*" * num)) // 因为此处num变量只使用了一次,而且只是进行简单的计算,所以可以省略参数列表,使用_替代参数 println((1 to 10).map("*" * _))
在scala和spark的源代码中,大量使用到了柯里化。为了后续方便阅读源代码,我们需要来了解下柯里化。
定义
柯里化(Currying)是指将原先接受多个参数的方法转换为多个只有一个参数的参数列表的过程。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Yitd5lcH-1625207556241)(/assets/1552811606951.png)]
柯里化过程解析
示例
示例说明
参考代码
// 柯里化:实现对两个数进行计算 def calc_carried(x:Double, y:Double)(func_calc:(Double, Double)=>Double) = { func_calc(x, y) } def main(args: Intrray[String]): Unit = { println(calc_carried(10.1, 10.2){ (x,y) => x + y }) println(calc_carried(10, 10)(_ + _)) println(calc_carried(10.1, 10.2)(_ * _)) println(calc_carried(100.2, 10)(_ - _)) }
闭包其实就是一个函数,只不过这个函数的返回值依赖于声明在函数外部的变量。
可以简单认为,就是可以访问不在当前作用域范围的一个函数。
示例一
定义一个闭包
val y=10 val add=(x:Int)=>{ x+y } println(add(5)) // 结果15
add函数就是一个闭包
示例二
柯里化就是一个闭包
def add(x:Int)(y:Int) = { x + y }
上述代码相当于
def add(x:Int) = { (y:Int) => x + y }
隐式转换和隐式参数是scala非常有特色的功能,也是Java等其他编程语言没有的功能。我们可以很方便地利用隐式转换来丰富现有类的功能。后面在编写Akka并发编程、Spark SQL、Flink都会看到隐式转换和隐式参数的身影。
所谓隐式转换,是指以implicit关键字声明的带有单个参数的方法。它是自动被调用的,自动将某种类型转换为另外一种类型。
使用步骤
示例
示例说明
使用隐式转换,让File具备有read功能——实现将文本中的内容以字符串形式读取出来
步骤
参考代码
class RichFile(val file:File) { // 读取文件为字符串 def read() = { Source.fromFile(file).mkString } } object RichFile { // 定义隐式转换方法 implicit def file2RichFile(file:File) = new RichFile(file) } def main(args: Array[String]): Unit = { // 加载文件 val file = new File("./data/1.txt") // 导入隐式转换 import RichFile.file2RichFile // file对象具备有read方法 println(file.read()) }
前面,我们手动使用了import来导入隐式转换。是否可以不手动import呢?
在scala中,如果在当前作用域中有隐式转换方法,会自动导入隐式转换。
示例:将隐式转换方法定义在main所在的object中
class RichFile(val f:File) { // 将文件中内容读取成字符串 def read() = Source.fromFile(f).mkString } object ImplicitConvertDemo { // 定义隐式转换方法 implicit def file2RichFile(f:File) = new RichFile(f) def main(args: Array[String]): Unit = { val f = new File("./data/textfiles/1.txt") // 调用的其实是RichFile的read方法 println(f.read()) } }
方法可以带有一个标记为implicit的参数列表。这种情况,编译器会查找缺省值,提供给该方法。
定义
[!NOTE]
- 和隐式转换一样,可以使用import手动导入隐式参数
- 如果在当前作用域定义了隐式值,会自动进行导入
示例
示例说明
参考代码
// 使用implicit定义一个参数 def quote(what:String)(implicit delimiter:(String, String)) = { delimiter._1 + what + delimiter._2 } // 隐式参数 object ImplicitParam { implicit val DEFAULT_DELIMITERS = ("<<<", ">>>") } def main(args: Array[String]): Unit = { // 导入隐式参数 import ImplicitParam.DEFAULT_DELIMITERS println(quote("李雷和韩梅梅")) }
elimiter:(String, String)) = {
delimiter._1 + what + delimiter._2
}
// 隐式参数
object ImplicitParam {
implicit val DEFAULT_DELIMITERS = ("<<<", “>>>”)
}
def main(args: Array[String]): Unit = {
// 导入隐式参数
import ImplicitParam.DEFAULT_DELIMITERS
println(quote("李雷和韩梅梅"))
}
?
原文:https://blog.51cto.com/u_15277063/2984806