scala----函数式编程
-
方法与函数
在scala中,方法和函数几乎可以等同,他们的定义,使用,运行机制都一样(方法可以转化为函数),只是函数的使用方式更加的灵活多样
def main(args: Array[String]): Unit = { // 函数f1,通过方法转化为函数得来 val f1 = sum _ //方法名后面有个空格 // 函数f2,通过定义匿名函数得来 val f2 = (n1:Int,n2:Int) => { // 函数体 n1+n2 } println(f1) println(f2) } // 方法 def sum(n1: Int, n2: Int) = { n1 + n2 } 打印f1与f2的结果: <function2> <function2>
-
函数式编程
把函数当做一等公民,充分利用函数,支持的函数的多种使用方式.可以像变量一样,即作为函数的参数使用,也可以将函数赋值给一个变量,函数的创建不用依赖于类或者对象,而在java中函数的创建则要依赖于类,抽象类,或者接口.
-
面向对象编程是以对象为基础的编程方式.
-
scala中将函数式编程与面向对象编程融合到一起
函数
函数的定义
基本语法
def 函数名 ([参数名:参数类型],...)[[:返回值类型] =] {
函数体
return 返回值
}
- 函数声明关键字是def (definition)
- 参数列表可以没有,如果有,多个参数使用逗号间隔
- 返回值可以没有
- 返回值的形式:
- :返回值类型 = 确定的返回值类型
- = 不确定的返回值类型
- 不写,表示没有返回值,return不生效,
- 如果没有return,默认执行到最后一行的结果作为返回值
函数的调用机制
现有理解:
一个java程序一定会有一个main函数作为程序入口,在内存中每一个函数都会生成一个栈(数据结构),当程序运行到从main函数中调用其他的函数时,会在内存中生成对应的栈,在栈里处理并得出结果然后返回到main函数的栈.
递归
一个函数在函数体内有调用本身
不能使用匿名函数写递归:
def main(args: Array[String]): Unit = {
val digui = (n : Int) => {
if (n > 2 ){
digui(n-1)
}
println(n)
}
digui(4)
}
运行报错:
Error:(7, 8) recursive value digui needs type
digui((Int)(n-1))
正确案例:
def main(args: Array[String]): Unit = {
digui(4)
}
def digui (n : Int){
if (n>2){
digui(n-1)
}
println(n)
}
结果:
2
3
4
运行的过程分析:
- 从mian函数开始执行,第二行产生新的栈(1)n=4
- 在(1)中进入if选项产生新的栈(2)n=3
- 在(2)中进入if选项产生新的栈(3)n=2
- 在(3)中跳过if选项,执行输出打印2
- 返回到(2)打印3
- 返回到(1)打印4
- 返回到main方法的栈
总结:
- 执行一个函数时,就创建一个新的受保护的独立空间(新的函数栈)
- 函数的局部变量是独立的相互不受影响
- 递归必须向退出递归的条件逼近,否则就是无限递归
- 当一个函数执行完毕,或者遇到return,就会返回,遵守谁调用,就将结果返回给谁的原则
递归练习:
-
使用递归的方式,求出斐波那契数列
给一个整数n,求出它的斐波那契数列
使用函数的注意事项:
-
return 可以省略,如果使用就不能使用自动识别返回类型,要使用确认的返回值类型,或者无返回值。使用无返回值,return 失效(相当于指定返回值为unit)
使用递归时尤为要注意这一点,使用return不能使用返回值的自动类型识别 -
scala 任何语法结构里都可以嵌套其他的语法结构。如方法嵌套方法,这时内部的方法实际上是外部方法所在类的private final 方法(方法名后添加$n)
-
函数/方法可以给形参赋初始值,当调用函数时使用的参数会覆盖初始值,如果没有参数则使用初始值(只有具有初始值的形式参数在被调用时可以省略)
//给形参赋初始值 def sum1(n1:Int = 10,n2:Int)={ n1+n2 }
可变参数
-
java的可变参数
类型后加"…"可变参数必须在最后
public class Test { private int sum(int ... ints){ int sum =0; for (int i :ints ) { sum+=i; } return sum; } private void sum1(int n , String ... name){ for (String na:name ) { System.out.println(na); } } public static void main(String[] args) { Test te = new Test(); int sum = te.sum(1, 2, 3, 4); System.out.println(sum); System.out.println("-------多种类型的参数-------"); te.sum1(sum,"hello","world","bigdate"); } }
-
scala中的可变参数
类型后加*,args实际上是个序列 ,同java相同也要放在参数列表最后def main(args: Array[String]): Unit = { val s = this.sum(10, 1, 2, 3, 4) println(s) } def sum(n1: Int, args: Int*) = { var sum = n1 println(s"args.length: ${args.length}") for (i <- args) { sum += i } sum }
过程
无返回值的函数,或者返回值为Unit的函数
lazy
惰性函数,先将函数或者集合存放在缓冲区,当用到函数后者集合时在进行计算.
java没有提供惰性函数(但是可以实现惰性),scala直接提供
惰性计算(尽可能延迟表达式求值)是许多函数式编程语言的特性.惰性集合在需要时提供其元素,无需预先计算.
优点
- 可以将耗时的计算推迟到绝对需要的时候
- 可以创建无限个集合,只要他们继续收到请求,就会提供元素
- 使代码更高效
-
java实现方式:
当调用函数时 并没有只申明了对象,没有创建,调用getPropert时创建对象
public class LazyDemo { private String property; public String getProperty() { if (property == null) { property = initProperty(); } return property; } private String initProperty() { return "create Property"; } }
-
scala
当函数返回值被声明为lazy时,函数的执行将被推迟,直到我们首次对此取值,该函数才会执行,这种函数我们称之为惰性函数,在java的某些框架代码中称之为懒加载(延迟加载)object LazyDemo { def main(args: Array[String]): Unit = { lazy val res = sum(1, 2) println("---------sum() has bean called--------") println(res) } def sum(n1: Int, n2: Int) = { println("sum() have done ") n1 + n2 } } 结果: ---------sum() has bean called-------- sum() have done 3 可以看到,sum函数的内容是在res被调用的时候执行的,
-
注意事项:
-
lazy不修饰var类型的变量,说明lazy是线程安全的
-
不但是在调用函数时,加了lazy,会导致函数的执行被推迟,我们在声明一个变量时,如果给申明了lazy,那么变量值得的分配也会推迟,比如:lazy val i = 10
scala> lazy val a = 88 a: Int = <lazy> scala> a res26: Int = 88
-
-
使用场景:并不会立即使用的函数或者集合
-
异常
scala中使用try-catch处理异常,使用方法与java差不多,不同的是scala中的异常都是运行异常,取消了"编译期异常"
复习java中的try-catch-finally执行顺序:
public class Test {
public static void main(String[] args) {
System.out.println(Test.getInt());
}
public static int getInt() {
int a = 0;
try {
int num = 1 / 0;
System.out.println("alter Arithmetic Exception");
a = 1;
} catch (ArithmeticException e) {
System.out.println("in catch block");
a = 2;
return a;
} finally {
System.out.println("in finally block");
a = 3;
}
System.out.println("alter finally block");
a = 4;
return a;
}
}
数据结果:
in catch block
in finally block
2
修改为无异常结果:
alter Arithmetic Exception
in finally block
alter finally block
4
说明:
try块中捕获异常:
- 发生异常后的代码不再执行
- 跳转到catch块
- 跳转到finally块
- 若catch中存在return,现将return的结果存放在缓冲区,finally执行完毕后再执行
- finally块后的代码不再执行
try块中无异常
- try块执行完毕后跳转到finally块
- finally块执行完毕后,执行后面的代码
关于catch捕获多个异常:
- 需要使用多个catch块
- 范围小的异常放在前面,否则报错
finally不论是否有异常都会执行,所以通常用于释放资源
scala处理异常
同java不同的是,捕获多个异常不适用多个catch而是在catch块中使用cacse(只有一个catch,有多个case)
即使具体的异常放在大范围的异常后面也不会报错
object exceptionDemo {
def main(args: Array[String]): Unit = {
try{
val a = 10/0
}catch{
case ex : ArithmeticException => println("catch Arithmetic Exception")
case ex: Exception => println("catch Exception")
}finally {
println("scala finally")
}
}
}
throw,自定义异常,使用方法与java类似
object exceptionDemo {
def main(args: Array[String]): Unit = {
try{
val test = exceptionDemo()
}catch {
case exception: Exception => println(s"catch exception , massage:${exception.getMessage}")
}
}
def exceptionDemo()={
throw new Exception("find new Exception!")
}
}
finally子句同java相同,不论是否有异常都会执行,一般用于对象的清理
throws,声明异常,有助于调用者提前处理异常,避免程序异常终止,
scala中可以使用注解的方式抛出异常
def main(args: Array[String]): Unit = {
try{
// 提前使用try-catch包裹
fun()
}catch {
case ex:NumberFormatException => println("数字格式错误")
}
}
@throws(classOf[NumberFormatException])
def fun() ={
"abc".toInt
}
控制结构和函数练习:
System.currentTimeMillis()获取时间
-
打印金字塔
def pyramid(n:Int=9,count:Int=0):Unit={ if(count<n){ pyramid(n,count+1) } println(" "*count+"*"*(2*(n-count)-1)) }
-
判断正负
def signum(n:Int):Int={ var signum=0 if (n==0){ return signum } signum=n>>31 if ((signum) ==0) { signum +=1 }else{ signum } return signum }
-
函数空的表达式{}的值是"()",类型是Unit
-
下面的循环改为scala写法
for(int i =10 ; i>=0;i--)System.out.println(i);
scala:
for ( i <- (0 to 10).reverse ) { println(i) }
(0 to 10)是一个range,调用range的reverse()函数,使其倒序
-
编写一个过程countdown(n:Int),打印从n到0的数字
def countDown(n:Int)={ for ( i <- (0 to n).reverse) { println(i) } }
-
用循环计算一个字符串中所有字符的Unicode代码的乘积
def productOfString(str:String)={ var sum =str.head.toLong for ( ch <- str.tail ) { sum*=ch.toLong } sum }
-
用递归实现上面的需求
使用递归要注意的是,代码要趋于结束def productOfString2(str:String):Long={ var sum :Long= 1 if (str.length>=1){ sum=str.head.toLong sum*=productOfString2(str.tail) } return sum }
-
使用StringOps的foreach方式
def productOfString3(str:String)={ var sum:Long = str.head.toLong str.tail.foreach(x=> {sum*=x.toLong}) sum }
-
递归计算x的n次方,n可以为负数
def powerDemo(x:Int,n:Int):Double={ var a =x var b = n.abs if (n>0){ pwerDemo2(a,b) }else{ 1.0/pwerDemo2(a,b) } } def pwerDemo2(x:Int,n:Int):Int={ var a =x var b =n if (b>=0){ if (b==0) a= 1 a*=pwerDemo2(a,b-1) } a }