Scala方法和函数
函数式编程:函数是Scala的核心
方法是类的一部分,属于定义在类中的函数
定义函数
//怎么定义一个函数以及传参
object FunctionDemo01 {
def main(args: Array[String]): Unit = {
val i: Int = square(5)
// println(i)
// printName("zhangsan")
println(f2(2))
println(f3(2, 2))
println(f4(2, 2))
println(f5(2, 2))
}
//定义一个函数
def square(x: Int) = {
println(x)
x * x
}
//返回值类型为unit的函数 可以省略=
def printName(x: String) {
println(x)
}
//定义一个匿名函数
val f1 = (x: Int) => {
x * x
}
//定义一个函数2
val f2:Int => Int=(x:Int)=>{
x*x
}
//定义一个有两个参数的函数
val f3=(x:Int,y:Int)=>{x+y}
val f4:((Int,Int)=>Int)={(x:Int,y:Int)=>x+y}
//不写参数名
val f5:(Int,Int)=>Int=(_+_)
//怎么给变量赋函数
scala> def square(x: Int) = {
| println(x)
| x * x
| }
square: (x: Int)Int
scala> val f2=square _
f2: Int => Int = <function1>
函数调用
-
传值调用
传值调用时,参数只在调用时计算一次,后续重复使用计算的结果**
def square(x: Int): Int = { println(x) //3 x * x //计算3*3 } square(1+2) //先计算1+2
-
传名调用
传名调用****时,参数在调用时不会计算,只有真正用到参数时才计算
def square(x: => Int): Int = { println(x) //计算1+2 x * x //计算(1+2)*(1+2) } square(1+2) //调用时不计算
案例
定义一个没有返回值的函数,实现输入一个整数,打印金字塔。例如输入10,打印如下
object function extends App {
def readInt(x:Int)={
for (i:Int <- 1 to x) {
for (j: Int <- 5 - i to 0 by -1) {
print(" ")
}
for (k: Int <- 1 to 2 * i - 1) {
print("*")
}
println()
}
}
readInt(5)
}
命名函数
- 通常情况下,传入参数与函数定义的参数列表一一对应
命名参数允许使用任意顺序传入参数
def printName(first:String, last:String) = {
println(first + " " + last)
}
//Prints "John Smith"
printName("John","Smith")
printName(first = "John",last = "Smith")
printName(last = "Smith",first = "John") //任意顺序
参数缺省值
- Scala函数允许指定参数的缺省值,从而允许在调用函数时不指明该参数
def printName(first:String="John", last:String="Smith") = {
println(first + " " + last)
}
//Prints "John Smith"
printName()
printName(“zhang”)
printName(“zhang”,”san”)
匿名函数
-
指不含函数名称的函数
-
匿名函数定义
-
“=>”左边为参数列表 (参数列表)=>{函数体}
-
“=>”右边为函数体
-
如果函数体包括多条语句,应使用“{}”包含
(x:Int)=>x*x (x:Int)=>{println(x);x*x} () => { System.getProperty("user.dir") } val f1=(x:Int)=>{println(x);x*x} //将匿名函数赋值给变量f1 f1(2)
-
高阶函数
定义
- 高阶函数可以将其他函数作为参数或者使用函数作为输出结果
- 常用高阶函数
- map
- foreach
- filter
- fold、foldLeft、foldRight
- reduce
- zip
- flatten
- flatMap
-
定义一个高阶函数:
object HigherDemo01 { def main(args: Array[String]): Unit = { //定义一个普通函数 def square(x:Int)=x*x val f0=(x:Int)=>x*x //定义一个高阶函数 def dosquare(f:Int=>Int,p:Int)=f(p) //=f0(2) println(dosquare(f0, 2)) //将一个函数作为返回值的高阶函数 def dosquare2()={(x:Int)=>x*x} //调用一个函数作为返回值的高阶函数 println(dosquare2()(3)) } }
-
常用高阶函数测试——scala shell界面
scala> val l = List(1,2,3,4,5,6,7,8,9) l: List[Int] = List(1, 2, 3, 4, 5, 6, 7, 8, 9) scala> l.map((_,1)) res0: List[(Int, Int)] = List((1,1), (2,1), (3,1), (4,1), (5,1), (6,1), (7,1), (8,1), (9,1)) scala> l.map(x=>x*2) res1: List[Int] = List(2, 4, 6, 8, 10, 12, 14, 16, 18) //用_变形 scala> l.map(_*2) res10: List[Int] = List(2, 4, 6, 8, 10, 12, 14, 16, 18) scala> l.foreach(println) 1 2 3 4 5 6 7 8 9 scala> l.map(x=>x*2).foreach(println) 2 4 6 8 10 12 14 16 18 scala> l.filter(x=>x%2==0) res7: List[Int] = List(2, 4, 6, 8) scala> l.fold(0)((sum,i)=>{println(sum,i);sum+i}) (0,1) (1,2) (3,3) (6,4) (10,5) (15,6) (21,7) (28,8) (36,9) res12: Int = 45 scala> l.foldLeft(0)((sum,i)=>{println(sum,i);sum+i}) (0,1) (1,2) (3,3) (6,4) (10,5) (15,6) (21,7) (28,8) (36,9) res13: Int = 45 scala> l.foldRight(0)((sum,i)=>{println(sum,i);sum+i}) (9,0) (8,9) (7,17) (6,24) (5,30) (4,35) (3,39) (2,42) (1,44) res14: Int = 45 scala> l.reduce(_+_) res15: Int = 45 scala> l.reduce((x,y)=>{println(x,y);x+y}) (1,2) (3,3) (6,4) (10,5) (15,6) (21,7) (28,8) (36,9) res16: Int = 45 scala> val list1 = List(1,2,3,4) list1: List[Int] = List(1, 2, 3, 4) scala> val list2 = List("a","b","c","d") list2: List[String] = List(a, b, c, d) scala> list1.zip(list2) res17: List[(Int, String)] = List((1,a), (2,b), (3,c), (4,d)) scala> val list4 = List(List(1,2),List(3,4),List(5,6)) list4: List[List[Int]] = List(List(1, 2), List(3, 4), List(5, 6)) scala> list4.flatten res23: List[Int] = List(1, 2, 3, 4, 5, 6) scala> list4.flatMap(_.map(_*2)) res25: List[Int] = List(2, 4, 6, 8, 10, 12)
-
小练习高阶函数写wordcount
scala> val list6 = List("hello spark","hello world","hello scala","hello hadoop") list6: List[String] = List(hello spark, hello world, hello scala, hello hadoop) scala> list6.map(_.split(" ")).flatten res27: List[String] = List(hello, spark, hello, world, hello, scala, hello, hadoop) //同样的可以直接用flatMap scala> list6.flatMap(_.split(" ")) res30: List[String] = List(hello, spark, hello, world, hello, scala, hello, hadoop) scala> list6.flatMap(_.split(" ")).map((_,1)) res31: List[(String, Int)] = List((hello,1), (spark,1), (hello,1), (world,1), (hello,1), (scala,1), (hello,1), (hadoop,1)) scala> list6.flatMap(_.split(" ")).map((_,1)).groupBy(_._1) res37: scala.collection.immutable.Map[String,List[(String, Int)]] = Map(world -> List((world,1)), hadoop -> List((hadoop,1)), spark -> List((spark,1)), scala -> List((scala,1)), hello -> List((hello,1), (hello,1), (hello,1), (hello,1))) scala> list6.flatMap(_.split(" ")).map((_,1)).groupBy(_._1).mapValues(_.size) res39: scala.collection.immutable.Map[String,Int] = Map(world -> 1, hadoop -> 1, spark -> 1, scala -> 1, hello -> 4) scala> list6.flatMap(_.split(" ")).map((_,1)).groupBy(_._1).mapValues(_.size).foreach(println) (world,1) (hadoop,1) (spark,1) (scala,1) (hello,4)
中置表达式
- 中置(缀)表达式是对只有一个参数的方法调用约定
- 当方法只有一个参数时,可以省略“.”和“()”
//示例1
1 to 10
//实际调用
1.to(10)
//示例2
List(1,2,3).map(x=>x+1).foreach(println)
//使用中置表达式
val f=(x:Int)=>x+1
List(1,2,3) map f foreach println
函数嵌套
-
Scala函数内可以定义函数,函数内的函数也称局部函数或者内嵌函数
举例 写个递归阶乘函数
object Demo07 { def main(args: Array[String]): Unit = { //定义一个外层函数 def factorial(i:Int):Int={ //定义一个嵌套函数 //5 5*4*3*2*1 def fact(i:Int,acc:Int):Int={ if (i<=1){ acc }else{ fact(i-1,i*acc) //fact(4,5*acc) //fact(3,4*acc) //fact(2,3*acc) //fact(1,2*acc) //acc } } fact(i,acc=1) } println(factorial(4)) } }
柯里化函数
柯里化函数也叫多参数列表函数,本身就是指把接受多个参数的函数变换成接受一个单一参数 (最初函数的第一个参数 )的函数,并且返回接受余下的参数且返回结果的新函数的技术。简单理解就是改变函数的形式,不改变函数的功能。看上去有点鸡肋,但是如果和隐式参数结合使用就能 大大简化代码,同时也降低了一定的代码可读性。
-
函数可以定义多个参数列表,当使用较少的参数列表调用多参数列表的函数时,会产生一个新的函数,该函数接收剩余的参数列表作为其参数。这被称为柯里化
//单参数列表 def modN(n: Int,x: Int) = ((x % n) == 0) //多参数列表 def modN(n: Int)(x: Int) = ((x % n) == 0) //新函数接收剩余的参数列表作为其参数 def f1(x: Int) = modN(10)(x) def f2(n: Int) = modN(n)(10) def f3 = modN(10)(_)
隐式参数
-
方法可以具有隐式参数列表,由参数列表开头的implicit 关键字标记
- implict只能修改最尾部的参数列表,应用于其全部参数
- Scala可自动传递正确类型的隐式值
- 通常与柯里化函数结合使用
object Demo09 { def main(args: Array[String]): Unit = { //定义一个隐式参数 def sum(x:Int)(implicit y:Int)= x+y implicit var a=10 println(sum(20)) } }
scala> val i:Int = 3.5 <console>:11: error: type mismatch; found : Double(3.5) required: Int val i:Int = 3.5 ^ //定义隐式函数 scala> implicit def doubletint(x:Double)=x.toInt warning: there was one feature warning; re-run with -feature for details doubletint: (x: Double)Int scala> val i:Int = 3.5 i: Int = 3
闭包
- 闭包是依照包含自由变量的函数字面量在运行时创建的函数值
- 闭包是对函数本身及其所使用的自由变量的统一定义
- 闭包的变量绑定
- 闭包可捕获自由变量的变化
- 闭包对捕获变量作出的改变在闭包之外也可见
var y=1
//定义一个闭包
def add(x:Int) ={y=y+1;x+y}
add(10)
println(y)