scala函数式编程

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 返回值
}
  1. 函数声明关键字是def (definition)
  2. 参数列表可以没有,如果有,多个参数使用逗号间隔
  3. 返回值可以没有
  4. 返回值的形式:
    • :返回值类型 = 确定的返回值类型
    • = 不确定的返回值类型
    • 不写,表示没有返回值,return不生效,
  5. 如果没有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

运行的过程分析:

  1. 从mian函数开始执行,第二行产生新的栈(1)n=4
  2. 在(1)中进入if选项产生新的栈(2)n=3
  3. 在(2)中进入if选项产生新的栈(3)n=2
  4. 在(3)中跳过if选项,执行输出打印2
  5. 返回到(2)打印3
  6. 返回到(1)打印4
  7. 返回到main方法的栈

总结:

  1. 执行一个函数时,就创建一个新的受保护的独立空间(新的函数栈)
  2. 函数的局部变量是独立的相互不受影响
  3. 递归必须向退出递归的条件逼近,否则就是无限递归
  4. 当一个函数执行完毕,或者遇到return,就会返回,遵守谁调用,就将结果返回给谁的原则


递归练习:

  1. 使用递归的方式,求出斐波那契数列

    给一个整数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直接提供

惰性计算(尽可能延迟表达式求值)是许多函数式编程语言的特性.惰性集合在需要时提供其元素,无需预先计算.

优点

  1. 可以将耗时的计算推迟到绝对需要的时候
  2. 可以创建无限个集合,只要他们继续收到请求,就会提供元素
  3. 使代码更高效
  • 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被调用的时候执行的,
    
    • 注意事项:

      1. lazy不修饰var类型的变量,说明lazy是线程安全的

      2. 不但是在调用函数时,加了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块中捕获异常:

  1. 发生异常后的代码不再执行
  2. 跳转到catch块
  3. 跳转到finally块
  4. 若catch中存在return,现将return的结果存放在缓冲区,finally执行完毕后再执行
  5. finally块后的代码不再执行

try块中无异常

  1. try块执行完毕后跳转到finally块
  2. finally块执行完毕后,执行后面的代码

关于catch捕获多个异常:

  1. 需要使用多个catch块
  2. 范围小的异常放在前面,否则报错

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()获取时间

  1. 打印金字塔

    def pyramid(n:Int=9,count:Int=0):Unit={
        if(count<n){
          pyramid(n,count+1)
        }
        println(" "*count+"*"*(2*(n-count)-1))
      }
    
  2. 判断正负

    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
      }
    
  3. 函数空的表达式{}的值是"()",类型是Unit

  4. 下面的循环改为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()函数,使其倒序

  5. 编写一个过程countdown(n:Int),打印从n到0的数字

    def countDown(n:Int)={
        for ( i <- (0 to n).reverse) {
          println(i)
        }
      }
    
  6. 用循环计算一个字符串中所有字符的Unicode代码的乘积

      def productOfString(str:String)={
        var sum =str.head.toLong
        for ( ch <- str.tail ) {
          sum*=ch.toLong
        }
        sum
      }
    
  7. 用递归实现上面的需求
    使用递归要注意的是,代码要趋于结束

    def productOfString2(str:String):Long={
        var sum :Long= 1
        if (str.length>=1){
          sum=str.head.toLong
        sum*=productOfString2(str.tail)
        }
        return sum
      }
    
  8. 使用StringOps的foreach方式

    def productOfString3(str:String)={
        var sum:Long = str.head.toLong
        str.tail.foreach(x=> {sum*=x.toLong})
        sum
      }
    
  9. 递归计算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
      }
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值