1. 方法
Scala 中使用 def 语句定义方法, val 语句定义函数。
定义方法的通用格式 :def functionName ([参数列表]) : [return type] = { 方法体 } 。
如果一个方法有返回值,方法的最后一条语句的返回值,一定要和方法的返回值类型保持一致。
如果不写等于号和方法主体,那么方法会被隐式声明为抽象(abstract),包含它的类型也是一个抽象类型。
1.1 main方法
1 2 3 4 5 6 7 8 9 10 11 12
| object Test1{ def main(args: Array[String]) { println("hello world") } }
object app1 extends Application { println("hello world") }
|
1.2 方法的定义
1.2.1 定义带返回值的方法
- 方法的返回值不需要加return,方法的最后一行作为整个方法的返回值
- 定义方法时 “:” 后面表示方法的返回值,如果定义了方法的返回值,当返回类型不一致时代码会编译不通过
- 可以忽略掉方法的返回值类型,scala会根据最后一条语句的返回值推断出方法的返回值类型
1 2 3 4 5 6 7
| def m1(x:Int,y:Int):Int = { x + y } def m2(x: Int, y: Int) { x + y }
|
1.2.2 定义没有返回值的方法
- 可以使用Unit来标注,表现为 “()”类似于java中的void
- 也在参数列表括号后面不加 “=”,直接添加方法体{}, 我们称这种方法为过程
1 2 3 4 5 6 7 8 9
| def m3(x: Int, y: Int): Unit = { x + y }
def m3_1(x: Int, y: Int) { x + y }
|
1.2.3 不定义参数名称只参数类型
先定义方法参数列表类型,具体的参数名称在方法体中,函数式编程中经常会用到 在spark 源码中大量出现
1 2 3 4 5
| def m4: (Int, Int, Int) => Int = { (x, y, z) => { x + y + z } }
|
1.2.4 定义无参方法
- 定义无参函数时带括号,调用时带不带括号都可以
- 定义无参函数时不带括号,调用时一定不能带括号
1 2 3 4 5 6 7 8
| def m6() = { println("hello") }
def m6_1 = { println("hello") }
|
1.2.5 柯理化的两种定义方式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| def m5(x: Int)(y: Int) = { x + y }
def m5_1(x: Int) = (y: Int) => { x + y }
def sum(a:Int, b:Int) = { a + b }
def sum(a:Int)(b:Int) = { a + b }
def sum(a:Int) = { (b:Int)=> a + b }
|
1.2.6 定义递归方法
定义递归方法时要求我们必须写明方法的返回值,不能省略,否则会报错。
1 2 3 4 5 6 7 8
| def m7(num: Int): Int = { if (num <= 0) { 0 } else { m7(num - 1) } }
|
1.2.7 定义带可变参数的方法
1 2 3 4 5 6 7 8 9 10 11 12
|
def m10(nums:Int*):Int={ var sum = 0 for(num <- nums){ sum += num } sum }
|
1.2.8 定义带默认参数的方法
有时我们调用某些方法时,不希望给出具体的值,而是希望使用参数自身默认的值,此时就在定义方法时使用默认参数
在调用方法的时候,赋值是从左往右以此赋值,所以需要把没有默认值的参数放在前面,或者带参数名调用
1 2 3 4 5 6 7 8 9 10 11 12 13
|
def m11(age:Int,name:String = "小明",sex:String ="男")={ printf("name = %s, age= %s, sex= %s \n",name,age,sex) } 调用方式: m11(name="小明",sex="男",age = 12) 或 m11(12)
|
1.2.9 方法的总结
| 定义方式 |
调用方式 |
返回值 |
| def f() { return .. } |
f, f()皆可 |
始终返回:Unit |
| def f() = … |
f, f()皆可 |
返回Unit或者值 |
| def f = … |
f |
返回Unit或者值 |
1.3 方法的调用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| m6()
m6_1
println("hello world" toUpperCase)
println("hello world".toUpperCase)
println("hello world".toUpperCase())
println("hello world" indexOf "w")
println("hello world".indexOf("w"))
println("hello world" indexOf("w"))
println("hello world" substring (0, 5))
println("hello world".substring(0, 5))
this.m6()
|
2. 函数
函数的地位和一般的变量是同等的,可以作为函数的参数,可以作为返回值
传入函数的任何输入是只读的,比如一个字符串,不会被改变,只会返回一个新的字符串。
官方并没有明确的给出方法和函数的区别,包括我们在网上看的一些常用的资料也不会很详细的区分两者的区别
2.1 函数的定义
2.1.1 定义通用格式的函数
1 2 3
| val f1 = (x:Int,y:Int) =>{ x + y }
|
2.1.2 定义函数的参数列表类型
先定义函数的参数列表类型,具体的函数参数在函数体中定义
1 2 3 4 5
| val f2:(Int,Int,Int)=>Int={ (x,y,z) => { x + y + z } }
|
2.1.3 定义映射式函数
- 映射式定义,是一种特殊的定义,相当于数学中的映射关系。
- 也可以看成是没有参数的函数,返回一个匿名函数,调用的时候是调用这个返回的匿名函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| def f3 : Int => Double = { case 1 => 0.1 case 2 => 0.2 case _ => 0.0 }
def f3_1 : Option[String] => String = { case Some(x) => x case None => null }
def f3_2:(Int,Int)=>Int = _+_
def f3_3:Int=>Int = 30+
def f3_4:Int=>Int = 30+_
|
2.1.4 定义偏函数
偏函数(partially applied function)即用 ““(下划线)代替1个或多个参数的函数,使用 ““ 代替参数时需要指定参数的类型
partial函数是一个正常函数中抽出来的一部分(参数不全),故称为partial函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| val f4 = sum _
(sum _)
val f4_1 = sum(10,_,20)
val f4_2 = sum(10,_:Int,20)
(sum(10,_:Int,10))
val f4_3 = sum(_:Int,100,_:Int)
(sum(_:Int,100,_:Int))
|
2.1.5 定义匿名函数 (有参)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| ((i:Int)=> i*i)(3)
((i:Int, j:Int) => i+j)(3, 4)
(10*)(2)
((x:Int)=>10*x)(2)
(10+)(2)
((x:Int)=>10+x)(2) /12
(List("a","b","c") mkString)("=")
val f5 = (i:Int)=> i*i
println(f5(3))
val l1 = List(1,2,3,4).map(f5) println(l1)
|
2.1.6 定义匿名函数 (无参)
2.1.7 定义匿名函数 (无参无返回值)
- 无返回值: ((命名参数列表)=>Unit)(参数列表)
1 2 3 4
| (() => Unit)
(() => {println("hello"); 20*10} )()
|
注:大量的匿名函数增加了代码的阅读难度。看到符号 “=>” 就要想到函数
2.1.8 定义匿名函数 (匿名参数)
- 匿名函数中的匿名参数使用 “_”
- 多个下划线指代多个参数,而不是单个参数的重复使用。
- 第一个下划线代表第一个参数,第二个下划线代表第二个,如此类推。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
|
((i:Int, j:Int) => i+j)(3, 4)
((_:Int) + (_:Int))(3,4)
def sum(x:Int,y:Int,z:Int) = x+y+z
val sum1 = sum _
val sum2 = sum(1000,_:Int,100)
sum1(1,2,3)
sum2(5)
|
2.2 函数传递
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| val foreachFun : (Int)=>Unit={ (x)=>{ println (s"x^2 = ${x*x}") } }
val filterFun = (x:Int) =>{ x > 3 }
val arr = Array(1,2,3,4,5)
arr.foreach(foreachFun)
arr.filter(filterFun).foreach(println)
arr.foreach(x => println (s"x^2 = ${x*x}"))
|
2.3 函数作为参数
1 2 3 4 5 6 7 8 9 10 11 12
| def f6(x:Int, y:Int, m:(Int, Int)=>Int) = m(x,y) println(f6(3,4, (x,y)=>x+y)) println(f6(3,4,(x,y) => scala.math.sqrt(x*x+y*y).toInt))
def f7(x:Int, y:Int, m: =>Unit) = { println(x*y); m } f7(3,4,println("end"))
def f8(f2:Int=>Int) = f2(5) println(f8(100-)) println(f8(100+))
|
2.4 函数作为返回值
1 2 3 4 5 6 7 8
| def f9(s: String): (Int => String) = { n: Int => { s * n } }
println(f9("*")(5))
|
3. 函数式编程
首选使用val,用var之前要仔细考虑是否真正需要。
1 2 3 4 5 6 7 8 9 10
|
var s = "" if ( args.length > 0 ) { s = args(0) }
val s = if ( args.length > 0 ) args(0) else ""
|
1 2 3
2 4 6
3 6 9
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
|
for(i<-1 to 3; j<-1 to 3) { print( i * j + " "); if (j==3){ println } }
def ff(n:Int) = 1 to 3 map ( _*n) mkString " " println(1 to 3 map ff mkString "\n")
|
4. 递归
使用递归的原因:
很多数学关系、逻辑关系本身就是递归描述的。
函数式编程不鼓励用变量循环,而是用递归。
递归包含递归出口和递归体:
- 递归出口:即递归的终止条件
- 递归体:即和前面或后面的值之间的关系
1 2 3 4 5 6 7 8
|
def pow(n: BigInt, m: BigInt): BigInt = if (m == 0) 1 else pow(n, m - 1) * n
val p = pow(2, 5) println(p)
|
5. 尾递归
定义:函数尾(最后一条语句)是递归调用的函数。
tail-recursive会被优化成循环,所以没有堆栈溢出的问题。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| def nn1(n:Int):BigInt = if (n==0) 1 else nn1(n-1)*n println(nn1(1000)) println(nn1(10000)) println(nn1(5)) nn1(5) {5 * nn1(4) } {5 * {4 * nn1(3) }} {5 * {4 * {3 * nn1(2) }}} {5 * {4 * {3 * {2 * nn1(1) }}}} {5 * {4 * {3 * {2 * 1}}}} {5 * {4 * {3 * 2}}} {5 * {4 * 6}} {5 * 24} 120
def nn2(n:Int, rt:BigInt):BigInt = if (n==0) rt else nn2(n-1, rt*n) println(nn2(1000,1)) println(nn2(10000,1)) println(nn2(5,1)) nn2(5, 1) nn2(4, 1*5=5) nn2(3, 4*5=20) nn2(2, 3*20=60) nn2(1, 2*60=120) 120
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| def prime(n:Int) = { 2 to math.sqrt(n).toInt forall (n%_!=0) }
def next(p:Int) = { p+1 to 2*p find prime get }
def f(p:Int, i:Int):Int = if (i==5) p else f(next(p), i+1)
println(f(2,1))
def f2(p:Int):Stream[Int] = p #:: f2(next(p))
f2(2).take(10001).last
|
- 使用递归计算斐波那契数列(后一个数是前两个数的和)
1 2 3 4 5 6 7 8 9 10 11 12
| def fib2(n1:BigInt, n2:BigInt, i:Int, n:Int):BigInt ={ if (i==n) { n1 } else { fib2(n2, n1+n2, i+1, n) } } def fib(n:Int) = fib2(1,1,0,n) 0 to 10 map fib fib(100-1)
|
6. 方法与函数的转换
1 2 3 4 5 6 7 8 9 10
| def m1( x : Int , y : Int ) = { x + y }
val f1 = m1 _ println(f1)
val sum = f1(1,2) println(sum)
|