Scala 中函数与方法的区别很是小,若是函数做为某个对象的成员,这样的函数被称为方法,不然就是一个正常的函数。java
// 定义方法 def multi1(x:Int) = {x * x} // 定义函数 val multi2 = (x: Int) => {x * x} println(multi1(3)) //输出 9 println(multi2(3)) //输出 9
也可使用 def
定义函数:git
def multi3 = (x: Int) => {x * x} println(multi3(3)) //输出 9
multi2
和 multi3
本质上没有区别,这是由于函数是一等公民,val multi2 = (x: Int) => {x * x}
这个语句至关因而使用 def
预先定义了函数,以后赋值给变量 multi2
。github
上面咱们说过 multi2
和 multi3
本质上是同样的,那么做为函数它们是什么类型的?二者的类型实际上都是 Int => Int
,前面一个 Int 表明输入参数类型,后面一个 Int 表明返回值类型。编程
scala> val multi2 = (x: Int) => {x * x} multi2: Int => Int = $$Lambda$1092/594363215@1dd1a777 scala> def multi3 = (x: Int) => {x * x} multi3: Int => Int // 若是有多个参数,则类型为:(参数类型,参数类型 ...)=>返回值类型 scala> val multi4 = (x: Int,name: String) => {name + x * x } multi4: (Int, String) => String = $$Lambda$1093/1039732747@2eb4fe7
在 Scala 中函数是一等公民,这意味着不只能够定义函数并调用它们,还能够将它们做为值进行传递:闭包
import scala.math.ceil object ScalaApp extends App { // 将函数 ceil 赋值给变量 fun,使用下划线 (_) 指明是 ceil 函数但不传递参数 val fun = ceil _ println(fun(2.3456)) //输出 3.0 }
在 Scala 中你没必要给每个函数都命名,如 (x: Int) => 3 * x
就是一个匿名函数:函数
object ScalaApp extends App { // 1.匿名函数 (x: Int) => 3 * x // 2.具名函数 val fun = (x: Int) => 3 * x // 3.直接使用匿名函数 val array01 = Array(1, 2, 3).map((x: Int) => 3 * x) // 4.使用占位符简写匿名函数 val array02 = Array(1, 2, 3).map(_ * 3) // 5.使用具名函数 val array03 = Array(1, 2, 3).map(fun) }
在 Java 中若是你想要传递可变长度的参数,须要使用 String ...args
这种形式,Scala 中等效的表达为 args: String*
。oop
object ScalaApp extends App { def echo(args: String*): Unit = { for (arg <- args) println(arg) } echo("spark","hadoop","flink") } // 输出 spark hadoop flink
向函数传递参数时候能够指定具体的参数名。大数据
object ScalaApp extends App { def detail(name: String, age: Int): Unit = println(name + ":" + age) // 1.按照参数定义的顺序传入 detail("heibaiying", 12) // 2.传递参数的时候指定具体的名称,则没必要遵循定义的顺序 detail(age = 12, name = "heibaiying") }
在定义函数时,能够为参数指定默认值。spa
object ScalaApp extends App { def detail(name: String, age: Int = 88): Unit = println(name + ":" + age) // 若是没有传递 age 值,则使用默认值 detail("heibaiying") detail("heibaiying", 12) }
var more = 10 // addMore 一个闭包函数:由于其捕获了自由变量 more 从而闭合了该函数字面量 val addMore = (x: Int) => x + more
如上函数 addMore
中有两个变量 x 和 more:scala
按照定义:在建立函数时,若是须要捕获自由变量,那么包含指向被捕获变量的引用的函数就被称为闭包函数。
这里须要注意的是,闭包捕获的是变量自己,便是对变量自己的引用,这意味着:
// 声明 more 变量 scala> var more = 10 more: Int = 10 // more 变量必须已经被声明,不然下面的语句会报错 scala> val addMore = (x: Int) => {x + more} addMore: Int => Int = $$Lambda$1076/1844473121@876c4f0 scala> addMore(10) res7: Int = 20 // 注意这里是给 more 变量赋值,而不是从新声明 more 变量 scala> more=1000 more: Int = 1000 scala> addMore(10) res8: Int = 1010
自由变量可能随着程序的改变而改变,从而产生多个副本,可是闭包永远指向建立时候有效的那个变量副本。
// 第一次声明 more 变量 scala> var more = 10 more: Int = 10 // 建立闭包函数 scala> val addMore10 = (x: Int) => {x + more} addMore10: Int => Int = $$Lambda$1077/1144251618@1bdaa13c // 调用闭包函数 scala> addMore10(9) res9: Int = 19 // 从新声明 more 变量 scala> var more = 100 more: Int = 100 // 建立新的闭包函数 scala> val addMore100 = (x: Int) => {x + more} addMore100: Int => Int = $$Lambda$1078/626955849@4d0be2ac // 引用的是从新声明 more 变量 scala> addMore100(9) res10: Int = 109 // 引用的仍是第一次声明的 more 变量 scala> addMore10(9) res11: Int = 19 // 对于全局而言 more 仍是 100 scala> more res12: Int = 100
从上面的示例能够看出从新声明 more
后,全局的 more
的值是 100,可是对于闭包函数 addMore10
仍是引用的是值为 10 的 more
,这是由虚拟机来实现的,虚拟机会保证 more
变量在从新声明后,原来的被捕获的变量副本继续在堆上保持存活。
定义函数时候支持传入函数做为参数,此时新定义的函数被称为高阶函数。
object ScalaApp extends App { // 1.定义函数 def square = (x: Int) => { x * x } // 2.定义高阶函数: 第一个参数是类型为 Int => Int 的函数 def multi(fun: Int => Int, x: Int) = { fun(x) * 100 } // 3.传入具名函数 println(multi(square, 5)) // 输出 2500 // 4.传入匿名函数 println(multi(_ * 100, 5)) // 输出 50000 }
咱们上面定义的函数都只支持一个参数列表,而柯里化函数则支持多个参数列表。柯里化指的是将原来接受两个参数的函数变成接受一个参数的函数的过程。新的函数以原有第二个参数做为参数。
object ScalaApp extends App { // 定义柯里化函数 def curriedSum(x: Int)(y: Int) = x + y println(curriedSum(2)(3)) //输出 5 }
这里当你调用 curriedSum 时候,其实是连着作了两次传统的函数调用,实际执行的柯里化过程以下:
x
的 Int 型参数,返回一个用于第二次调用的函数,假设 x
为 2,则返回函数 2+y
;y
,并计算并返回值 2+3
的值。想要得到柯里化的中间返回的函数其实也比较简单:
object ScalaApp extends App { // 定义柯里化函数 def curriedSum(x: Int)(y: Int) = x + y println(curriedSum(2)(3)) //输出 5 // 获取传入值为 10 返回的中间函数 10 + y val plus: Int => Int = curriedSum(10)_ println(plus(3)) //输出值 13 }
柯里化支持多个参数列表,多个参数按照从左到右的顺序依次执行柯里化操做:
object ScalaApp extends App { // 定义柯里化函数 def curriedSum(x: Int)(y: Int)(z: String) = x + y + z println(curriedSum(2)(3)("name")) // 输出 5name }
更多大数据系列文章能够参见 GitHub 开源项目: 大数据入门指南