zoukankan      html  css  js  c++  java
  • 大数据技术之_16_Scala学习_04_函数式编程-基础+面向对象编程-基础

    第五章 函数式编程-基础5.1 函数式编程内容说明5.1.1 函数式编程内容5.1.2 函数式编程授课顺序5.2 函数式编程介绍5.2.1 几个概念的说明5.2.2 方法、函数、函数式编程和面向对象编程关系分析图5.2.3 函数式编程小结5.3 为什么需要函数5.4 函数的定义5.4.1 函数的定义5.4.2 快速入门案例5.5 函数的调用机制5.5.1 函数的调用过程5.5.2 函数的递归调用5.5.3 递归练习题5.6 函数注意事项和细节讨论5.7 函数练习题5.8 过程5.8.1 基本概念5.8.2 注意事项和细节说明5.9 惰性函数5.9.1 看一个应用场景5.9.2 画图说明(大数据推荐系统)5.9.3 Java 实现懒加载的代码5.9.4 惰性函数介绍5.9.5 案例演示5.9.6 注意事项和细节5.10 异常5.10.1 介绍5.10.2 Java 异常处理回顾5.10.3 Java 异常处理的注意点5.10.4 Scala 异常处理举例5.10.5 Scala 异常处理小结5.11 函数的练习题第六章 面向对象编程-基础6.1 类与对象6.1.1 Scala 语言是面向对象的6.1.2 快速入门-面向对象的方式解决养猫问题6.1.3 类和对象的区别和联系6.1.4 如何定义类6.1.5 属性6.1.6 属性/成员变量6.1.7 属性的高级部分6.1.8 如何创建对象6.1.9 类和对象的内存分配机制(重要)6.2 方法6.2.1 基本说明和基本语法6.2.2 方法的调用机制原理6.2.3 方法练习题6.3 类与对象应用实例6.4 构造器6.4.1 看一个需求6.4.2 回顾-Java 构造器的介绍+基本语法+特点+案例6.4.3 Scala 构造器的介绍+基本语法+快速入门6.4.4 Scala 构造器注意事项和细节6.5 属性高级6.5.1 构造器参数6.5.2 Bean 属性6.6 Scala 对象创建的流程分析6.7 作业03


    第五章 函数式编程-基础

    5.1 函数式编程内容说明

    5.1.1 函数式编程内容

    函数式编程-基础
      1、函数定义/声明
      2、函数运行机制
      3、递归【难点:最短路径,邮差问题,背包问题,迷宫问题,回溯】
      4、过程
      5、惰性函数和异常

    函数式编程-高级
      6、值函数(函数字面量)
      7、高阶函数
      8、闭包
      9、应用函数
      10、柯里化函数,抽象控制…

    5.1.2 函数式编程授课顺序

      1、在 scala 中,函数式编程和面向对象编程融合在一起,学习函数式编程式需要 oop 的知识,同样学习 oop 需要函数式编程的基础。[矛盾]
      2、二者关系如下图:
      


      3、授课顺序:函数式编程基础 -> 面向对象编程 -> 函数式编程高级

    5.2 函数式编程介绍

    5.2.1 几个概念的说明

      在学习 Scala 中将方法、函数、函数式编程和面向对象编程明确一下:
      1、在 scala 中,方法函数几乎可以等同(比如他们的定义、使用、运行机制都一样的),只是函数的使用方式更加的灵活多样。
      2、函数式编程是从编程方式(范式)的角度来谈的,可以这样理解:函数式编程把函数当做一等公民,充分利用函数、支持的函数的多种使用方式
      比如:在 Scala 当中,函数是一等公民,像变量一样,既可以作为函数的参数使用,也可以将函数赋值给一个变量,函数的创建不用依赖于类或者对象,而在 Java 当中,函数的创建则要依赖于类、抽象类或者接口。
      3、面向对象编程是以对象为基础的编程方式。
      4、在 scala 中函数式编程和面向对象编程融合在一起了。
    示例代码如下:

    package com.atguigu.chapter05

    object Method2Function {
      def main(args: Array[String]): Unit = {
        // 传统的方式使用方法
        // 先创建一个对象
        val dog = new Dog
        println(dog.sum(10, 20))

        // 方法转成函数后使用函数
        val f1 = dog.sum _
        println("f1=" + f1) // f1=<function2>
        println("f1=" + f1(50, 60))

        // 直接写一个函数并使用函数
        // 格式:val f2 = (Int, Int) => {}
        val f2 = (n1: Int, n2: Int) => {
          n1 + n2 // 函数体
        }
        println("f2=" + f2) // f2=<function2>
        println("f2=" + f2(80, 90))
      }
    }

    class Dog {
      // 方法
      def sum(n1: Int, n2: Int): Int = {
        n1 + n2 // 方法体
      }
    }

    输出结果如下:

    30
    f1=<function2>
    f1=110
    f2=<function2>
    f2=170

    5.2.2 方法、函数、函数式编程和面向对象编程关系分析图

    在学习 Scala 中将方法、函数、函数式编程和面向对象编程关系分析图如下:

    5.2.3 函数式编程小结

      1、“函数式编程”是一种“编程范式”(programming paradigm)。
      2、它属于“结构化编程”的一种,主要思想是把运算过程尽量写成一系列嵌套的函数调用。
      3、函数式编程中,将函数也当做数据类型,因此可以接受函数当作输入(参数)和输出(返回值)。
      4、函数式编程中,最重要的就是函数。

    5.3 为什么需要函数

    学习一个技术或者知识点的流程:

    5.4 函数的定义

    5.4.1 函数的定义

    5.4.2 快速入门案例

    使用函数完全前面的案例。
    示例代码如下:

    package com.atguigu.chapter05

    object FunDemo01 {
      def main(args: Array[String]): Unit = {
        println("" + getRes(10, 20, '+'))
      }

      // 定义一个函数/方法
      def getRes(n1: Int, n2: Int, oper: Char) = { // 返回值形式2: = 表示返回值类型不确定,使用类型推导完成。
        if (oper == '+') {
          // return n1 + n2 // return 关键字可以写可以不写
          n1 + n2
        } else if (oper == '-') {
          n1 - n2
        } else {
          // 返回 null
          null
        }
      }
    }

    5.5 函数的调用机制

    5.5.1 函数的调用过程

      为了让大家更好的理解函数调用机制,看1个案例,并画出示意图,这个很重要,比如 getSum 计算两个数的和,并返回结果。

    5.5.2 函数的递归调用

    注意:Struts2 中的拦截器的底层实现机制就是把一个对象放到堆中,然后不停的开栈去指向该对象的内存地址,每一个栈都有机会去修改该对象的值。

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

    5.5.3 递归练习题

    题1:斐波那契数,请使用递归的方式,求出斐波那契数1,1,2,3,5,8,13… 给你一个整数n,求出它的斐波那契数是多少?
    示例代码如下:

    package com.atguigu.chapter05.recursive

    import scala.io.StdIn

    /**
      * 题1:斐波那契数,请使用递归的方式,求出斐波那契数1,1,2,3,5,8,13... 给你一个整数n,求出它的斐波那契数是多少?
      * 思路:f(1)=1, f(2)=1, f(3)=f(2)+f(1)=1+1=2, f(4)=f(2)+f(3)=1+2=3, ..., f(n)=f(n-1)+f(n-2)
      */
    object Exercise01 {
      def main(args: Array[String]): Unit = {

        println("请输入一个正整数:")
        val n = StdIn.readInt()
        printf("%d的斐波那契数是:%d", n, fbn(n))
      }

      def fbn(n: Int): Int = {
        if (n == 1 || n == 2) {
          1
        } else {
          fbn(n - 1) + fbn(n - 2)
        }
      }
    }

    题2:求函数值,已知 f(1)=3; f(n) = 2*f(n-1)+1; 请使用递归的思想编程,求出 f(n) 的值?
    示例代码如下:

    package com.atguigu.chapter05.recursive

    import scala.io.StdIn

    /**
      * 题2:求函数值,已知 f(1)=3; f(n) = 2*f(n-1)+1; 请使用递归的思想编程,求出 f(n) 的值?
      * n=1, f(1)=3
      * n=2, f(2)=2*f(1)+1=7
      * n=3, f(3)=2*f(2)+1=15
      *
      */
    object Exercise02 {
      def main(args: Array[String]): Unit = {
        println("请输入一个正整数:")
        val n = StdIn.readInt()
        printf("f(%d) 的值是:%d", n, f(n))
      }

      def f(n: Int): Int = {
        if (n == 1) {
          3
        } else {
          2 * f(n - 1) + 1
        }
      }
    }

    题3:猴子吃桃子问题:有一堆桃子,猴子第一天吃了其中的一半,并再多吃了一个!以后每天猴子都吃其中的一半,然后再多吃一个。当到第十天时,想再吃时(还没吃),发现只有1个桃子了。问题:最初共多少个桃子?
    示例代码如下:

    package com.atguigu.chapter05.recursive

    import com.atguigu.chapter05.recursive.Exercise02.f

    import scala.io.StdIn

    /**
      * 题3:猴子吃桃子问题:有一堆桃子,猴子第一天吃了其中的一半,并再多吃了一个!
      * 以后每天猴子都吃其中的一半,然后再多吃一个。当到第十天时,想再吃时(还没吃),发现只有1个桃子了。问题:最初共多少个桃子?
      *
      * day = 10 桃子有 1
      * day =  9 桃子有 (day10的桃子 + 1) *2
      * day =  8 桃子有 (day9 的桃子 + 1) *2
      *
      */
    object Exercise03 {
      def main(args: Array[String]): Unit = {
        println("最初共有:" + f(1) + "个桃子")
      }

      def f(n: Int): Int = {
        if (n == 10) {
          1
        } else {
          (f(n + 1) + 1) * 2
        }
      }
    }

    5.6 函数注意事项和细节讨论

      1、函数的形参列表可以是多个,如果函数没有形参,调用时可以不带()。

      2、函数的形参列表和返回值列表的数据类型可以是值类型和引用类型。【案例演示】
    示例代码如下:

    package com.atguigu.chapter05.fundetails

    object Details01 {
      def main(args: Array[String]): Unit = {
        val tiger = new Tiger
        val tiger2 = test01(10, tiger)
        println(tiger2.name) // tom
        println(tiger.name)  // tom
        println(tiger.hashCode() + " " + tiger2.hashCode()) // 2101440631 2101440631
      }

      // 2、函数的形参列表和返回值列表的数据类型可以是值类型和引用类型。
      def test01(n: Int, tiger: Tiger): Tiger = {
        println("n=" + n)
        tiger.name = "tom"
        tiger
        // return tiger // 3、Scala 中的函数可以根据函数体最后一行代码自行推断函数返回值类型。那么在这种情况下,return 关键字可以省略。
      }
    }

    class Tiger {
      var name = ""
    }

      3、Scala 中的函数可以根据函数体最后一行代码自行推断函数返回值类型。那么在这种情况下,return 关键字可以省略。 【案例同上】
      4、因为 Scala 可以自行推断,所以在省略 return 关键字的场合,返回值类型也可以省略。
      


      5、如果函数明确使用 return 关键字,那么函数返回就不能使用自行推断了,这时要明确写成 : 返回值类型 = ,当然如果你什么都不写,即使有 return,那么返回值为(),即这时 return 无效。
      6、如果函数明确声明无返回值(声明 Unit),那么函数体中即使使用 return 关键字也不会有返回值。
    示例代码如下:
    package com.atguigu.chapter05.fundetails

    object Details02 {
      def main(args: Array[String]): Unit = {

        println(getSum2(10, 30))  // ()

        println(getSum3(9, 9))    // ()

      }

      // 如果写了 return,那么返回值类型就不能省略。
      def getSum(n1: Int, n2: Int): Int = {
        return n1 + n2
      }

      // 如果返回值这里什么什么都没有写,即表示该函数没有返回值。
      // 这时 return 无效
      def getSum2(n1: Int, n2: Int) {
        return n1 + n2
      }

      // 如果函数明确声明无返回值(声明Unit),那么函数体中即使使用 return 关键字也不会有返回值。
      def getSum3(n1: Int, n2: Int): Unit = {
        return n1 + n2
      }
    }

      7、如果明确函数无返回值或不确定返回值类型,那么返回值类型可以省略(或声明为 Any)。
    示例代码如下:

    package com.atguigu.chapter05.fundetails

    object Details03 {
      def main(args: Array[String]): Unit = {

      }

      // 7、如果明确函数无返回值或不确定返回值类型,那么返回值类型可以省略(或声明为 Any)。
      def f3(s: String) = {
        if (s.length >= 3)
          s + "123"
        else
          3
      }

      def f4(s: String): Any = {
        if (s.length >= 3)
          s + "123"
        else
          3
      }
    }

      8、Scala 语法中任何的语法结构都可以嵌套其他语法结构(很灵活),即:函数中可以再声明/定义函数类中可以再声明类方法中可以再声明/定义方法
    示例代码如下:

    package com.atguigu.chapter05.fundetails

    object Details04 {
      def main(args: Array[String]): Unit = { // public void main(String[] args)

        def f1():Unit = { // private final void f1$1
          println("f1")
        }

        def sayok(): Unit = { // private final void sayok$1
          println("sayok~")
          def sayok(): Unit = { // private final void sayok$2
            println("sayok~~")
          }
        }

        println("ok")
      }

      def sayok(): Unit = { // public void sayok()
        println("sayok")
      }
    }

      9、Scala 函数的形参,在声明参数时,直接赋初始值(默认值),这时调用函数时,如果没有指定实参,则会使用默认值。如果指定了实参,则实参会覆盖默认值
    示例代码如下:

    package com.atguigu.chapter05.fundetails

    object Details05 {
      def main(args: Array[String]): Unit = {
        println(sayOk())      // jack ok!
        println(sayOk("tom")) // tom ok!
      }

      def sayOk(name: String = "jack"): String = {
        return name + " ok! "
      }
    }

      10、如果函数存在多个参数,每一个参数都可以设定默认值,那么这个时候,传递的参数到底是覆盖默认值,还是赋值给没有默认值的参数,就不确定了(默认按照声明顺序[从左到右])。在这种情况下,可以采用带名参数
    示例代码如下:

    package com.atguigu.chapter05.fundetails

    object Details06 {
      def main(args: Array[String]): Unit = {
        mysqlCon()
        mysqlCon("127.0.0.1", 7777) // 从左到右覆盖

        // 如果我们希望指定覆盖某一个默认值,则使用带名参数即可,比如只想修改用户名和密码,其他的不改
        mysqlCon(user = "tom", pwd = "1234")

        // 练习
        // f6("v2") // 报错,p2的值没有指定
        f6(p2 = "v2") // v1v2
      }

      def mysqlCon(add: String = "localhost", port: Int = 3306,
                   user: String = "root", pwd: String = "root"): Unit = {
        println("add=" + add)
        println("port=" + port)
        println("user=" + user)
        println("pwd=" + pwd)
      }

      def f6(p1: String = "v1", p2: String) {
        println(p1 + p2);
      }
    }

      11、scala 函数的形参默认是 val 的,因此不能在函数中进行修改。

      12、递归函数未执行之前是无法推断出来结果类型,在使用时必须有明确的返回值类型。
    示例代码如下:

      def f(n: Int) = { // 错误,递归不能使用类型推断,必须指定返回的数据类型。
        if (n <= 0)
          1
        else
          n * f(n - 1)
      }

      13、Scala 函数支持可变参数
    示例代码如下:

      // 支持0到多个参数
      def sum(args: Int*): Int = {
      }

      // 支持1到多个参数
      def sum(n1: Int, args: Int*): Int = {
      }

      说明:
      1、args 是集合, 通过 for 循环 可以访问到各个值。【args 是参数名,可以任意起】
      2、案例演示: 编写一个函数 sum,可以求出 1 到多个 int 的和。
      3、可变参数需要写在形参列表的最后。
    示例代码如下:

    package com.atguigu.chapter05.fundetails

    object VarParameters {
      def main(args: Array[String]): Unit = {
        println(sum(10, 20, 30)) // 这里可变参数为2个
      }

      // 支持1到多个参数
      def sum(n1: Int, args: Int*): Int = {
        println("args.length=" + args.length)
        var sum = n1
        for (item <- args) {
          sum += item
        }
        sum
      }
    }

    5.7 函数练习题

    判断下面的代码是否正确:
    示例代码如下:

      object Hello01 {
        def main(args: Array[String]): Unit = {
          def f1 = "venassa"
          println(f1) // 输出 venassa
        }
      }

      // 上面代码 def f1 = "venassa" 等价于
      def f1() = {
        "venassa"
      }
      // 说明:
      // 1、函数的形参列表可以是多个,如果函数没有形参,函数可以不带()。
      // 2、函数的函数体只有一行代码时,可以省略{}。

    5.8 过程

    5.8.1 基本概念

    基本介绍:
      将函数的返回类型为 Unit 的函数称之为过程(procedure),如果明确函数没有返回值,那么等号可以省略。

    案例说明:

      // f10 没有返回值,可以使用 Unit 来说明
      // 这时,这个函数我们也叫过程(procedure)
      def f10(name: String): Unit = { // 如果明确函数没有返回值,那么等号可以省略。
        println(name+ "hello")
      }

    5.8.2 注意事项和细节说明

      1、注意区分: 如果函数声明时没有返回值类型,但是有 = 号,可以进行类型推断最后一行代码。这时这个函数实际是有返回值的,该函数并不是过程。(这点在讲解函数细节的时候讲过的)
      2、开发工具的自动代码补全功能,虽然会自动加上 Unit,但是考虑到 Scala 语言的简单,灵活,最好不加。

    5.9 惰性函数

    5.9.1 看一个应用场景

      惰性计算(尽可能延迟表达式求值)是许多函数式编程语言的特性。惰性集合在需要时提供其元素,无需预先计算它们,这带来了一些好处。首先,您可以将耗时的计算推迟到绝对需要的时候。其次,您可以创造无限个集合,只要它们继续收到请求,就会继续提供元素。函数的惰性使用让您能够得到更高效的代码。Java 并没有为惰性提供原生支持,Scala 提供了。

    5.9.2 画图说明(大数据推荐系统)

    5.9.3 Java 实现懒加载的代码

    示例代码如下:

    package com.atguigu.chapter05;

    public class LazyDemo {

        private String property; // 属性也可能是一个数据库连接,文件等资源

        public String getProperty() {
            if (property == null) { // 如果没有初始化过,那么就进行初始化
                property = initProperty();
            }
            return property;
        }

        private String initProperty() {
            return "property";
        }
    }
    // 比如常用的【单例模式懒汉式】实现时就使用了上面类似的思路实现

    5.9.4 惰性函数介绍

      当函数返回值被声明为 lazy 时,函数的执行将被推迟,直到我们首次对此取值,该函数才会执行,这种函数我们称之为惰性函数。在 Java 的某些框架代码中称之为懒加载(延迟加载)

    5.9.5 案例演示

    示例代码如下:

    package com.atguigu.chapter05.mylazy

    object LazyDemo01 {
      def main(args: Array[String]): Unit = {
        lazy val res = sum(10, 20)
        println("----------")
        println("res=" + res) // 在要使用 res 前,才执行
      }

      def sum(n1: Int, n2: Int): Int = {
        println("sum() 执行了..")
        return n1 + n2
      }
    }

    输出结果如下:

    ----------
    sum() 执行了..
    res=30

    5.9.6 注意事项和细节

      1、lazy 不能修饰 var 类型的变量。
      2、不但是在调用函数时,加了 lazy,会导致函数的执行被推迟,我们在声明一个变量时,如果声明了 lazy,那么变量值的分配也会推迟。 比如 lazy val i = 10。

    5.10 异常

    5.10.1 介绍

      Scala 提供 try 块和 catch 块来处理异常。try 块用于包含可能出错的代码。catch 块用于处理 try 块中发生的异常。可以根据需要在程序中有任意数量的 try…catch 块。
      语法处理上和 Java 类似,但是又不尽相同。

    5.10.2 Java 异常处理回顾

    示例代码如下:

    package com.atguigu.chapter05.exception;

    public class JavaExceptionDemo {
        public static void main(String[] args) {
            try {
                // 可疑代码
                int i = 0;
                int b = 10;
                int c = b / i; // 执行代码时,会抛出 ArithmeticException 异常
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                // 最终要执行的代码
                System.out.println("java finally");
            }

            System.out.println("继续执行");
        }
    }

    输出结果如下:

    java finally
    继续执行
    java.lang.ArithmeticException: / by zero
        at com.atguigu.chapter05.exception.JavaExceptionDemo.main(JavaExceptionDemo.java:9)

    5.10.3 Java 异常处理的注意点

      1、java 语言按照 try-catch-catch…-finally 的方式来处理异常。
      2、不管有没有异常捕获,都会执行 finally,因此通常可以在 finally 代码块中释放资源。
      3、可以有多个 catch,分别捕获对应的异常,这时需要把范围小的异常类写在前面,把范围大的异常类写在后面,否则编译错误。会提示 "Exception 'java.lang.xxxxxx' has already been caught"。

    5.10.4 Scala 异常处理举例

    示例代码如下:

    package com.atguigu.chapter05.exception

    object ScalaExceptionDemo {
      def main(args: Array[String]): Unit = {
        try {
          val r = 10 / 0
        } catch {
          // 说明
          // 1. 在 scala 中只有一个 catch
          // 2. 在 catch 中有多个 case, 每个 case 可以匹配一种异常
          // 3. => 关键符号,表示后面是对该异常的处理代码块
          // 4. finally 最终要执行的代码
          case ex: ArithmeticException => { println("捕获了除数为零的算数异常") } // 当对该异常的处理代码块为一行时,{}可以省略
          case ex: Exception => println("捕获了异常")
        } finally {
          // 最终要执行的代码
          println("scala finally")
        }

        System.out.println("继续执行")
      }
    }

    输出结果如下:

    捕获了除数为零的算数异常
    scala finally
    继续执行

    5.10.5 Scala 异常处理小结

      1、我们将可疑代码封装在 try 块中。在 try 块之后使用了一个 catch 处理程序来捕获异常。如果发生任何异常,catch 处理程序将处理它,异常处理了程序将不会异常终止
      2、Scala 的异常的工作机制和 Java 一样,但是 Scala 没有“checked(编译期)” 异常,即 Scala 没有编译异常这个概念,异常都是在运行的时候捕获处理。
      3、Scala 用 throw 关键字,抛出一个异常对象。所有异常都是 Throwable 的子类型。throw 表达式是有类型的,就是 Nothing,因为 Nothing 是所有类型的子类型,所以 throw 表达式可以用在需要类型的地方。
    示例代码如下:

    package com.atguigu.chapter05.exception

    object ThrowDemo {
      def main(args: Array[String]): Unit = {
        // val res = test()
        // println(res.toString)
        // println("继续执行002") // 异常抛出了,但是没有被处理,后续程序不能执行

        // 如果我们希望在 test() 抛出异常后,后续代码可以继续执行,则我们需要如下处理
        try {
          test()
        } catch {
          case ex: Exception => {
            println("捕获到异常是:" + ex.getMessage)
            println("继续执行001")
          }
          case ex: ArithmeticException => println("得到一个算术异常(小范围异常)")
        } finally {
          // 写上对 try{} 中的资源的分配
        }

        println("继续执行002")
      }

      def test(): Nothing = {
        // Exception("异常出现")
        throw new ArithmeticException("算术异常")
      }
    }

    输出结果如下:

    捕获到异常是:算术异常
    继续执行001
    继续执行002

      4、在 Scala 里,借用了模式匹配的思想来做异常的匹配,因此,在 catch 的代码里,是一系列 case 子句来匹配异常。【前面案例可以看出这个特点,模式匹配我们后面详解】,当匹配上后 => 有多条语句可以换行写,类似 java 的 switch case x: 代码块…
      5、异常捕捉的机制与其他语言中一样,如果有异常发生,catch 子句是按次序捕捉的。因此,在 catch 子句中,越具体的异常越要靠前,越普遍的异常越靠后,如果把越普遍的异常写在前,把具体的异常写在后,在 scala 中也不会报错,但这样是非常不好的编程风格。
      6、finally 子句用于执行不管是正常处理还是有异常发生时都需要执行的步骤,一般用于对象的清理工作,这点和 Java 一样。
      7、Scala 提供了 throws 关键字来声明异常。可以使用方法定义声明异常。它向调用者函数提供了此方法可能引发此异常的信息。它有助于调用函数处理并将该代码包含在 try-catch 块中,以避免程序异常终止。在 scala 中,可以使用 throws 注释来声明异常。
    示例代码如下:

    package com.atguigu.chapter05.exception

    object ThrowsComment {
      def main(args: Array[String]): Unit = {
        f()
      }

      @throws(classOf[NumberFormatException]) // 等同于 Java 中 NumberFormatException.class
      def f() = {
        "abc".toInt
      }
    }

    输出结果如下:

    Exception in thread "main" java.lang.NumberFormatException: For input string: "abc"
        at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
        at java.lang.Integer.parseInt(Integer.java:580)
        at java.lang.Integer.parseInt(Integer.java:615)
        at scala.collection.immutable.StringLike$class.toInt(StringLike.scala:272)
        at scala.collection.immutable.StringOps.toInt(StringOps.scala:29)
        at com.atguigu.chapter05.exception.ThrowsComment$.f(ThrowsComment.scala:10)
        at com.atguigu.chapter05.exception.ThrowsComment$.main(ThrowsComment.scala:5)
        at com.atguigu.chapter05.exception.ThrowsComment.main(ThrowsComment.scala)

    5.11 函数的练习题

    1、函数可以没有返回值案例,编写一个函数,从终端输入一个整数打印出对应的金子塔。
    示例代码如下:

    package com.atguigu.chapter05.exercises

    import scala.io.StdIn

    /**
      * 1、函数可以没有返回值案例,编写一个函数,从终端输入一个整数打印出对应的金子塔。
      * 思路:本质是打印出所有的 n行m列 数据。 分别循环即可!
      */
    object Exercise01 {
      def main(args: Array[String]): Unit = {
        println("请输入一个整数n(n>=1):")
        val n = StdIn.readInt()
        printJin(n)
      }

      def printJin(n: Int): Unit = {
        for (i <- 1 to n) { // 行数
          for (j <- 1 to (n - i)) {
            printf("-")
          }
          for (j <- 1 to (2 * i - 1)) { // 列数
            printf("*")
          }
          for (j <- 1 to (n - i)) {
            printf("-")
          }
          println()
        }
      }
    }

    输出结果如下:

    请输入一个整数n(n>=1):
    5
    ----*----
    ---***---
    --*****--
    -*******-
    *********

    2、编写一个函数,从终端输入一个整数(1—9),打印出对应的乘法表。
    示例代码如下:

    package com.atguigu.chapter05.exercises

    import scala.io.StdIn

    /**
      * 2、编写一个函数,从终端输入一个整数(1—9),打印出对应的乘法表。
      */
    object Exercise01 {
      def main(args: Array[String]): Unit = {
        println("请输入数字(1-9)之间:")
        val n = StdIn.readInt()
        print99(n)
      }

      def print99(n: Int): Unit = {
        for (i <- 1 to n) {
          for (j <- 1 to i) {
            printf("%d * %d = %d ", j, i, j * i)
          }
          println()
        }
      }
    }

    3、编写函数,对给定的一个二维数组 (3×3) 转置,这个题讲数组的时候再完成。

        1 2 3       1 4 7            
        4 5 6       2 5 8               
        7 8 9       3 6 9

    第六章 面向对象编程-基础

    6.1 类与对象

    看一个养猫猫问题:
      张老太养了只猫猫:一只名字叫小白,今年3岁,白色。还有一只叫小花,今年10岁,花色。请编写一个程序,当用户输入小猫的名字时,就显示该猫的名字,年龄,颜色。如果用户输入的小猫名错误,则显示张老太没有这只猫猫。

    问题:
      1、猫有三个属性,类型不一样。
      2、如果使用普通的变量就不好管理。
      3、使用一种新的数据类型:
        (1) 可以管理多个不同类型的数据 [属性]。
        (2) 可以对属性进行操作 => 方法。

    类与对象的关系示意图

    6.1.1 Scala 语言是面向对象的

      1、Java 是面向对象的编程语言,由于历史原因,Java 中还存在着非面向对象的内容:基本类型,null,静态方法等。
      2、Scala 语言来自于 Java,所以天生就是面向对象的语言,而且 Scala 是纯粹的面向对象的语言,即在 Scala 中,一切皆为对象。
      3、在面向对象的学习过程中可以对比着 Java 语言学习。

    6.1.2 快速入门-面向对象的方式解决养猫问题

    示例代码如下:

    package com.atguigu.chapter06.oop

    object CatDemo {
      def main(args: Array[String]): Unit = {

        // 创建一只猫
        val cat = new Cat

        // 给猫的属性赋值
        // 说明
        // 1. cat.name = "小白" 其实不是直接访问属性,而是等价于 cat.name_$eq("小白")
        // 2. cat.name 等价于 cat.name()
        cat.name = "小白"
        cat.age = 3
        cat.color = "白色"

        printf(" 小猫的信息如下:%s %d %s", cat.name, cat.age, cat.color)
      }
    }

    /* 反编译查看源码
    public void main (String[] args) {
      Cat cat = new Cat ();

      cat.name_$eq ("小白");
      cat.age_$eq (3);
      cat.color_$eq ("白色");
    }
    */

    // 定义一个 Cat 类
    // 一个 class Cat 对应的字节码文件只有一个 Cat.class ,默认是public
    class Cat {
      // 定义/声明三个属性
      // 说明
      // 1. 当我们声明了 var name: String 时,同时在底层对应生成 private name
      // 2. 同时在底层会生成 两个 public 方法 public String name() 类似 => getter 和 public void name_$eq(String x$1) => setter
      var name: String = "" // Scala 中定义变量必须给初始值
      var age: Int = _ // 下划线表示给 age 一个默认值,如果是 Int 类型,默认就是 0
      var color: String = _ // 如果是 String 类型,默认值就是 null
    }

    /* 反编译查看源码
    public class Cat {
      private String name = "";
      private int age;
      private String color;

      public String name() {
        return this.name;
      }
      public void name_$eq(String x$1) {
        this.name = x$1;
      }
      public int age() {
        return this.age;
      }
      public void age_$eq(int x$1) {
        this.age = x$1;
      }
      public String color() {
        return this.color;
      }
      public void color_$eq(String x$1) {
        this.color = x$1;
      }
    }
    */

    输出结果如下:

    小猫的信息如下:小白 3 白色

    6.1.3 类和对象的区别和联系

    通过上面的案例和讲解我们可以看出:
      1、类是抽象的,概念的,代表一类事物,比如人类,猫类…
      2、对象是具体的,实际的,代表一个具体事物。
      3、类是对象的模板,对象是类的一个个体,对应一个实例。
      4、Scala 中类和对象的区别和联系 和 Java 是一样的。

    6.1.4 如何定义类


    我们可以通过反编译来看 scala 的类默认为 public 的特性。

    6.1.5 属性


    示例代码如下:

     

    class Dog {
      var name = "jack"
      var lover = new Fish
    }

    class Fish {

    }

    6.1.6 属性/成员变量


    示例代码如下:

     

    package com.atguigu.chapter06.oop

    object PropertyDemo {
      def main(args: Array[String]): Unit = {
        // val p1 = new Person
        // println(p1.Name)     // Null
        // println(p1.address)  // String 类型

        val a = new A
        println(a.var1) // null
        println(a.var2) // 0
        println(a.var3) // 0.0
        println(a.var4) // false

        // 不同对象的属性是独立,互不影响,一个对象对属性的更改,不影响另外一个
        // 创建两个对象
        var worker1 = new Worker
        worker1.name = "jack"
        var worker2 = new Worker
        worker2.name = "tom"
      }
    }

    class Person3 {
      var age: Int = 10   // 给属性赋初值,省略类型,会自动推导
      var sal = 8090.9
      var Name = null     // Name 是什么类型
      var address: String = null // ok
    }

    class A {
      var var1: String = _  // null   String 和 引用类型默认值是 null
      var var2: Byte = _    // 0
      var var3: Double = _  // 0.0
      var var4: Boolean = _ // false
    }

    class Worker {
      var name = ""
    }

    输出结果如下:

    null
    0
    0.0
    false

    6.1.7 属性的高级部分

      说明:属性的高级部分和构造器(构造方法/函数) 相关,我们把属性高级部分放到构造器那里讲解。

    6.1.8 如何创建对象


    示例代码如下:
    package com.atguigu.chapter06.oop

    object CreateObjDemo {
      def main(args: Array[String]): Unit = {
        val emp = new Emp // 此时的 emp 类型就是 Emp

        // 如果我们希望将子类对象,交给父类引用,这时就需要写上类型,不能省略!
        val emp1: Person = new Emp
      }
    }

    class Person {

    }

    class Emp extends Person {

    }

    6.1.9 类和对象的内存分配机制(重要)

    内存布局图:


    示例代码如下:
    package com.atguigu.chapter06.oop

    object MemState {
      def main(args: Array[String]): Unit = {
        val p2 = new Person2
        p2.name = "jack"
        p2.age= 10

        val p1 = p2
        println(p1 == p2) // true
        println("p2.age=" + p2.age) // 10
        println("p1.age=" + p1.age) // 10
      }
    }

    class Person2 {
      var name = ""
      var age: Int = _ // 如果是用下划线的方式给默认值,则属性必须指定类型,因为这有这样,scala 底层才能够进行类型推断
    }

    6.2 方法

    6.2.1 基本说明和基本语法


    示例代码如下:
    package com.atguigu.chapter06.method

    object MethodDemo01 {
      def main(args: Array[String]): Unit = {
        // 使用一把
        val dog = new Dog
        println(dog.cal(10, 20))
      }
    }

    class Dog {
      private var sal: Double = _
      var food: String = _

      def cal(n1: Int, n2: Int): Int = {
        return n1 + n2
      }
    }

    6.2.2 方法的调用机制原理

    6.2.3 方法练习题


    1~3题的示例代码如下:
    package com.atguigu.chapter06.method

    /**
      * 1、编写类(MethodExec),编写一个方法,方法不需要参数,在方法中打印一个10*8的矩形,在main方法中调用该方法。
      *
      * 2、修改上一个程序,编写一个方法中,方法不需要参数,计算该矩形的面积,并将其作为方法返回值。在main方法中调用该方法,接收返回的面积值并打印(结果保留小数点2位)。
      *
      * 3、修改上一个程序,编写一个方法,提供m和n两个参数,方法中打印一个m*n的矩形,再编写一个方法计算该矩形的面积(可以接收长len和宽width), 将其作为方法返回值。在main方法中调用该方法,接收返回的面积值并打印。
      */
    object MethodDemo02 {
      def main(args: Array[String]): Unit = {
        val m = new MethodExec
        m.printRect1()

        m.len = 1.2
        m.width = 3.4
        println("面积=" + m.area1())

        m.printRect2(5, 4)
        println("面积=" + m.area2(1.2, 3.4))
      }
    }

    class MethodExec {

      var len = 0.0
      var width = 0.0

      def printRect1(): Unit = {
        for (i <- 0 until 10) {
          for (j <- 0 until 8) {
            print("*")
          }
          println()
        }
      }

      def area1(): Double = {
        this.len * this.width.formatted("%.2f").toDouble
      }

      def printRect2(m: Int, n: Int): Unit = {
        for (i <- 0 until m) {
          for (j <- 0 until n) {
            printf("*")
          }
          println()
        }
      }

      def area2(len: Double,  Double): Double = {
        len * width.formatted("%.2f").toDouble
      }
    }

    输出结果如下:

    ********
    ********
    ********
    ********
    ********
    ********
    ********
    ********
    ********
    ********
    面积=4.08
    ****
    ****
    ****
    ****
    ****
    面积=4.08

    4~6题的示例代码原理同上1~3题,不在赘述!

    6.3 类与对象应用实例


    景区门票案例

    小狗案列的示例代码如下:
    package com.atguigu.chapter06.dogcase

    /**
      * 小狗案例
      *
      * 编写一个Dog类,包含name(String)、age(Int)、weight(Double)属性。
      * 类中声明一个say方法,返回String类型,方法返回信息中包含所有属性值。
      * 在另一个DogCaseTest类中的main方法中,创建Dog对象,并访问say方法和所有属性,将调用结果打印输出。
      */
    object DogCaseTest {
      def main(args: Array[String]): Unit = {
        val dog = new Dog
        dog.name = "泰斯特"
        dog.age = 2
        dog.weight = 50
        println(dog.say())
      }
    }

    class Dog {
      var name = ""
      var age = 0
      var weight = 0.0

      def say(): String = {
        "小狗的信息是:name=" + this.name + " age=" + this.age + " weight=" + this.weight
      }
    }

    输出结果如下:

    小狗的信息是:name=泰斯特    age=2   weight=50.0

    盒子案列、景区门票案例的示例代码原理同上小狗案例,不在赘述!

    6.4 构造器

    6.4.1 看一个需求

      前面我们在创建 Person 的对象时,是先把一个对象创建好后,再给他的年龄和姓名属性赋值,如果现在我要求,在创建人类的对象时,就直接指定这个对象的年龄和姓名,该怎么做? 这时就可以使用构造方法/构造器。

    6.4.2 回顾-Java 构造器的介绍+基本语法+特点+案例

    Java 构造器的介绍
      构造器(constructor)又叫构造方法,是类的一种特殊的方法,它的主要作用是完成对新对象的初始化

    Java 构造器的基本语法

    Java 构造器的特点

    Java 构造器的案例

    6.4.3 Scala 构造器的介绍+基本语法+快速入门

    Scala 构造器的介绍
      和 Java 一样,Scala 构造对象也需要调用构造方法,并且可以有任意多个构造方法(即 scala 中构造器也支持重载)。
      Scala 类的构造器包括: 主构造器辅助构造器

    Scala 构造器的基本语法

    Scala 构造器的快速入门
    示例代码如下:

    package com.atguigu.chapter06.constructor

    /**
      * Scala构造器的快速入门:创建Person对象的同时初始化对象的age属性值和name属性值
      */
    object ConstructorDemo01 {
      def main(args: Array[String]): Unit = {
        val p1 = new Person("bruce", 20)
        println(p1)
      }
    }

    class Person(inName: String, inAge: Int) {
      var name: String = inName
      var age: Int = inAge

      // 重写toString方法
      override def toString: String = {
        "name=" + this.name + " age=" + this.age
      }
    }

    输出结果如下:

    name=bruce    age=20

    6.4.4 Scala 构造器注意事项和细节

      1、Scala 构造器作用是完成对新对象的初始化,构造器没有返回值。
      2、主构造器的声明直接放置于类名之后。【可以反编译查看】
      3、主构造器会执行类定义中的所有语句(除掉函数部分),这里可以体会到 Scala 的函数式编程和面向对象编程融合在一起,即:构造器也是方法(函数),传递参数和使用方法和前面的函数部分内容没有区别。【案例演示+反编译查看-语法糖】
      4、如果主构造器无参数,小括号可省略,构建对象时调用的构造方法的小括号也可以省略。
      5、辅助构造器名称为 this(这个和 Java 是不一样的),多个辅助构造器通过不同参数列表进行区分, 在底层就是f构造器重载。【案例演示+反编译查看】
    示例代码如下:

    package com.atguigu.chapter06.constructor

    object ConstructorDemo02 {
      def main(args: Array[String]): Unit = {
        // val a = new A
        val aa = new A("jack")
        // 执行顺序:
        // 1、bbb  父类构造器
        // 2、A    子类主构造器
        // 3、aaa  子类辅助构造器
      }
    }

    class B {
      println("bbb")
    }

    class A extends B {
      println("A")
      def this(name: String) {
        this // 调用A的主构造器,其根本原因就是实现子类与父类之间的继承关系,不然继承关系就断了!!!
        println("aaa")
      }
    }

    输出结果如下:

    bbb
    A
    aaa

    示例代码如下:

    package com.atguigu.chapter06.constructor

    object ConstructorDemo03 {
      def main(args: Array[String]): Unit = {
        val p1 = new Person("scott")
        p1.showInfo()
      }
    }

    class Person() {
      var name: String = _
      var age: Int = _

      def this(name: String) {
        // 辅助构造器无论是直接或间接,最终都一定要调用主构造器,执行主构造器的逻辑
        // 而且需要放在辅助构造器的第一行[这点和 java 一样,java 中一个构造器要调用同类的其它构造器,也需要放在第一行]
        this() // 直接调用主构造器
        this.name = name
      }

      def this(name: String, age: Int) {
        this() // 直接调用主构造器
        this.name = name
        this.age = age
      }

      def this(age: Int) {
        this("匿名") // 间接调用主构造器,因为 def this(name: String) 中直接调用了主构造器
        this.age = age
      }

      def showInfo(): Unit = {
        println("person信息如下:")
        println("name=" + this.name)
        println("age=" + this.age)
      }
    }

    输出结果如下:

    person信息如下:
    name=scott
    age=0

      6、如果想让主构造器变成私有的,可以在()之前加上 private,这样用户只能通过辅助构造器来构造对象了。【反编译查看】
      7、辅助构造器的声明不能和主构造器的声明(即形参列表)一致,会发生错误(即构造器名重复)。

    6.5 属性高级

      前面我们讲过属性了,这里我们再对属性的内容做一个加强。

    6.5.1 构造器参数


    示例代码如下:
    package com.atguigu.chapter06.constructor

    object ConstructorDemo04 {
      def main(args: Array[String]): Unit = {
        val worker1 = new Worker1("smith1")
        worker1.name    // 不能访问 inName

        val worker2 = new Worker2("smith2")
        worker2.inName  // 可以访问 inName
        println("hello!")

        val worker3 = new Worker3("jack")
        worker3.inName = "mary"
        println(worker3.inName)
      }
    }

    // 1. 如果 主构造器是 Worker1(inName: String),那么 inName 就是一个局部变量。
    class Worker1(inName: String) {
      var name = inName
    }
    // 2. 如果 主构造器是 Worker2(val inName: String),那么 inName 就是 Worker2 的一个 private 的只读属性。
    class Worker2(val inName: String) {
      var name = inName
    }

    // 3. 如果 主构造器是 Worker3(var inName: String),那么 inName 就是 Worker3 的一个 private 的可以读写属性。
    class Worker3(var inName: String) {
      var name = inName
    }

    6.5.2 Bean 属性


    示例代码如下:
    package com.atguigu.chapter06.constructor

    import scala.beans.BeanProperty

    object BeanPropertyDemo {
      def main(args: Array[String]): Unit = {
        val car = new Car
        car.name = "宝马"
        println(car.name)

        // 使用 @BeanProperty 自动生成 getXxx() 和 setXxx()
        car.setName("奔驰")
        println(car.getName())
      }
    }

    class Car {
      @BeanProperty var name: String = null
    }

    6.6 Scala 对象创建的流程分析

    6.7 作业03

    1、一个数字如果为正数,则它的 signum 为1.0,如果是负数,则 signum 为-1.0,如果为0,则 signum 为0.0。编写一个函数来计算这个值。
    示例代码如下:

    package com.atguigu.chapter06.exercises

    import scala.io.StdIn

    /**
      * 1、一个数字如果为正数,则它的 signum 为1,如果是负数,则 signum 为-1,如果为0,则 signum 为0。编写一个函数来计算这个值。
      */
    object Exercise01 {
      def main(args: Array[String]): Unit = {
        println("请输入一个数字:")
        val n = StdIn.readDouble()
        println("该数的 signum 为:" + signum(n))
      }

      def signum(n: Double): Double = {
        if (n > 0) {
          1
        } else if (n < 0) {
          -1
        } else {
          0
        }
      }
    }

    输出结果如下:

    请输入一个数字:
    0
    该数的 signum 为:0.0

    请输入一个数字:
    5
    该数的 signum 为:1.0

    请输入一个数字:
    -3
    该数的 signum 为:-1.0

    2、一个空的块表达式{}的值是什么?类型是什么?
    示例代码如下:

    package com.atguigu.chapter06.exercises

    /**
      * 2、一个空的块表达式 {} 的值是什么?类型是什么?
      */
    object Exercise02 {
      def main(args: Array[String]): Unit = {
        val t = {}
        println("t=" + t) // t=()
        println(t.isInstanceOf[Unit]) // true
      }
    }

    3、针对下列 Java 循环编写一个 Scala 版本:

        for (int i=10; i>=0; i–-) {
            System.out.println(i);
        }

    示例代码如下:

    package com.atguigu.chapter06.exercises

    /**
      * 3、针对下列 Java 循环编写一个 Scala 版本:
      * for (int i=10; i>=0; i–-) {
      *   System.out.println(i);
      * }
      */
    object Exercise03 {
      def main(args: Array[String]): Unit = {
        // 方式一:
        for (i <- 0 to 10) {
          println("i=" + (10 - i))
        }

        println("----------")

        // 方式二:
        for (i <- 0 to 10 reverse) { // 逆序
          println("i=" + i)
        }

        // 定义一个 List 集合
        val list = List(1, 2, 3)
        println(list) // List(1, 2, 3)
        println(list.reverse) // List(3, 2, 1)
      }
    }

    4、编写一个过程 countdown(n: Int),打印从 n 到 0 的数字。
    示例代码如下:

    package com.atguigu.chapter06.exercises

    import scala.io.StdIn

    /**
      * 4、编写一个过程 countdown(n:Int),打印从 n 到 0 的数字。
      */
    object Exercise04 {
      def main(args: Array[String]): Unit = {

        val m=3
        val res1 = (0 to m).reverse
        println(res1) // Range(3, 2, 1, 0)

        // foreach
        // foreach 函数可以接收 (f: Int => U),即接收一个输入参数为 Int,输出参数为 Unit 的函数
        // 下面这句代码的含义是:
        // 1、将 res1 的每个元素依次遍历出来,传递给 println(x)
        // 调用系统的 println 函数
        res1.foreach(println)

        // 调用自定义的 println 函数
        res1.foreach(myPrintln)


        println("----------")
        println("请输入一个数字:")
        val n = StdIn.readInt()
        println("----------")
        countdown(n)
        println("----------")
        countdown2(n)
        println("----------")
        countdown3(n)
      }

      // 自定义一个 println 函数
      def myPrintln(n: Int): Unit = {
        println(n)
      }

      // 方式一:
      def countdown(n: Int): Unit = {
        for (i <- 0 to n) {
          println(n-i)
        }
      }

      // 方式二:
      def countdown2(n: Int): Unit = {
        for (i <- 0 to n reverse) {
          println(i)
        }
      }

      // 方式三:
      def countdown3(n: Int): Unit = {
        // 说明
        // 这里使用到高阶函数的特性
        (0 to n).reverse.foreach(println)
      }
    }

    5、编写一个 for 循环,计算字符串中所有字母的 Unicode 代码(toLong 方法)的乘积。举例来说,"Hello" 中所有字符串的乘积为 9415087488L。
    示例代码如下:

    package com.atguigu.chapter06.exercises

    import scala.io.StdIn

    /**
      * 5、编写一个 for 循环,计算字符串中所有字母的 Unicode 代码(toLong 方法)的乘积。举例来说,"Hello" 中所有字符串的乘积为 9415087488L。
      */
    object Exercise05 {
      def main(args: Array[String]): Unit = {
        println("请输入一行字符串:")
        val str = StdIn.readLine()
        println("该字符串中所有字母的 Unicode 代码的乘积为:" + unicode(str))

        unicode2()
      }

      // 方式一:
      def unicode(str: String): Long = {
        var res:Long = 1
        for (i <- 0 to str.length - 1) { // 索引从0开始
          var s = str.charAt(i).toLong
          res *= s
        }
        res
      }

      // 方式二:
      def unicode2() = {
        var res:Long = 1
        for (i <- "Hello") {
          res *= i.toLong
        }
        println("res=" + res)
      }
    }

    输出结果如下:

    请输入一行字符串:
    Hello
    该字符串中所有字母的 Unicode 代码的乘积为:9415087488
    res=9415087488

    6、同样是解决前一个练习的问题,请用 StringOps 的 foreach 方式解决。
    示例代码如下:

    package com.atguigu.chapter06.exercises

    import scala.io.StdIn

    /**
      * 6、同样是解决前一个练习的问题,请用 StringOps 的 foreach 方式解决。
      */
    object Exercise06 {
      def main(args: Array[String]): Unit = {
        var res1: Long = 1
        // 说明
        // 方式一:
        // "Hello".foreach((_) => {res *= _.toLong})
        "Hello".foreach(res1 *= _.toLong)
        println("res1=" + res1)

        // 方式二:
        var res2 = 1L
        "Hello".foreach(myCount)
        println("res1=" + res2)
        def myCount(char: Char): Unit = {
          res2 *= char.toLong
        }
      }
    }

    输出结果如下:

    res1=9415087488
    res1=9415087488

    7、编写一个函数 product(str: String),计算字符串中所有字母的 Unicode 代码(toLong 方法)的乘积。
    示例代码如下:

    package com.atguigu.chapter06.exercises

    import scala.io.StdIn

    /**
      * 7、编写一个函数 product(str: String),计算字符串中所有字母的 Unicode 代码(toLong 方法)的乘积。
      */
    object Exercise07 {
      def main(args: Array[String]): Unit = {
        println("请输入一行字符串:")
        val str = StdIn.readLine()
        println("该字符串中所有字母的 Unicode 代码的乘积为:" + product1(str))
        println("该字符串中所有字母的 Unicode 代码的乘积为:" + product2(str))
      }

      // 方式一:
      def product1(str: String): Long = {
        var multi = 1L
        for (i <- 0 to str.length - 1) { // 索引从0开始
          var s = str.charAt(i).toLong
          multi *= s
        }
        multi
      }

      // 方式二:
      def product2(str: String): Long = {
        var multi = 1L
        for (i <- str) {
          multi *= i.toLong
        }
        multi
      }
    }

    输出结果如下:

    请输入一行字符串:
    Hello
    该字符串中所有字母的 Unicode 代码的乘积为:9415087488
    该字符串中所有字母的 Unicode 代码的乘积为:9415087488

    8、把7练习中的函数改成递归函数。
    示例代码如下:

    package com.atguigu.chapter06.exercises

    /**
      * 8、把7练习中的函数改成递归函数。
      */
    object Exercise08 {
      def main(args: Array[String]): Unit = {
        println("res=" + product("Hello")) // res=9415087488

        println("Hello".take(1)) // H     获取的是该字符串的第一个字符串
        println("Hello".drop(1)) // ello  获取的是该字符串的除第一个字符串之外的剩余字符串
      }

      def product(str: String): Long = {
        if (str.length == 1) return str.charAt(0).toLong
        else str.take(1).charAt(0).toLong * product(str.drop(1))
      }
    }

    输出结果如下:

    res=9415087488
    H
    ello

    9、编写函数计算,其中 n 是整数,使用如下的递归定义:


    示例代码如下:
    package com.atguigu.chapter06.exercises

    /**
      * 9、编写函数计算,其中 n 是整数,使用如下的递归定义:
      */
    object Exercise09 {
      def main(args: Array[String]): Unit = {
        println(mi(2.5, 3))
      }

      // 递归的妙用:求 x 的 n 次方,厉害啊!!!
      def mi(x: Double, n: Int): Double = {
        if (n == 0) 1   // x 的 0 次方等于 1
        else if (n > 0) x * mi(x, n - 1)
        else 1 / mi(x, -n)
      }
    }

    注意:本题可以用于好好理解“递归”的妙用!!!

    Java 与 Scala 在函数层面上的不同体现:
    // 在 Java 中
    函数(接收参数)

    // 在 Scala 中
    集合.函数(函数)

    如下图所示:

  • 相关阅读:
    【坐在马桶上看算法】算法5:解密回文——栈
    【坐在马桶上看算法】算法4:队列——解密QQ号
    【坐在马桶上看算法】小哼买书
    python项目离线环境配置指南
    vis.js绘图库的一个BUG以及源码修正
    Python 访问soap服务
    一些教训
    Django 图片上传、存储与显示
    WAMP的一些配置修改
    Alpha阶段个人总结
  • 原文地址:https://www.cnblogs.com/chenmingjun/p/10604813.html
Copyright © 2011-2022 走看看