第五章 函数式编程-基础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\t", 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("\n小猫的信息如下:%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, width: 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 + "\tage=" + this.age + "\tweight=" + 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 + "\tage=" + 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 中
集合.函数(函数)

如下图所示:

大数据技术之_16_Scala学习_04_函数式编程-基础+面向对象编程-基础的更多相关文章

  1. 大数据技术之_16_Scala学习_09_函数式编程-高级

    第十三章 函数式编程-高级13.1 偏函数(partial function)13.1.1 提出一个需求,引出思考13.1.2 解决方式-filter + map 返回新的集合13.1.3 解决方式- ...

  2. 大数据技术之_16_Scala学习_01_Scala 语言概述

    第一章 Scala 语言概述1.1 why is Scala 语言?1.2 Scala 语言诞生小故事1.3 Scala 和 Java 以及 jvm 的关系分析图1.4 Scala 语言的特点1.5 ...

  3. 大数据技术之_16_Scala学习_08_数据结构(下)-集合操作+模式匹配

    第十一章 数据结构(下)-集合操作11.1 集合元素的映射-map11.1.1 map 映射函数的操作11.1.2 高阶函数基本使用案例1+案例211.1.3 使用 map 映射函数来解决11.1.4 ...

  4. 大数据技术之_16_Scala学习_02_变量

    第二章 变量2.1 变量是程序的基本组成单位2.2 Scala 变量的介绍2.2.1 概念2.2.2 Scala 变量使用的基本步骤2.3 Scala 变量的基本使用2.4 Scala 变量使用说明2 ...

  5. 大数据技术之_08_Hive学习_04_压缩和存储(Hive高级)+ 企业级调优(Hive优化)

    第8章 压缩和存储(Hive高级)8.1 Hadoop源码编译支持Snappy压缩8.1.1 资源准备8.1.2 jar包安装8.1.3 编译源码8.2 Hadoop压缩配置8.2.1 MR支持的压缩 ...

  6. 大数据技术之_16_Scala学习_05_面向对象编程-中级

    第七章 面向对象编程-中级7.1 包7.1.1 Java 中的包7.1.2 Scala 中的包7.1.3 Scala 包的特点概述7.1.4 Scala 包的命名7.1.5 Scala 会自动引入的常 ...

  7. 大数据技术之_16_Scala学习_06_面向对象编程-高级+隐式转换和隐式值

    第八章 面向对象编程-高级8.1 静态属性和静态方法8.1.1 静态属性-提出问题8.1.2 基本介绍8.1.3 伴生对象的快速入门8.1.4 伴生对象的小结8.1.5 最佳实践-使用伴生对象解决小孩 ...

  8. 大数据技术之_16_Scala学习_07_数据结构(上)-集合

    第十章 数据结构(上)-集合10.1 数据结构特点10.1.1 Scala 集合基本介绍10.1.2 可变集合和不可变集合举例10.2 Scala 不可变集合继承层次一览图10.2.1 图10.2.2 ...

  9. 大数据技术之_16_Scala学习_13_Scala语言的数据结构和算法_Scala学习之旅收官之作

    第十九章 Scala语言的数据结构和算法19.1 数据结构(算法)的介绍19.2 看几个实际编程中遇到的问题19.2.1 一个五子棋程序19.2.2 约瑟夫问题(丢手帕问题)19.2.3 其它常见算法 ...

随机推荐

  1. SQL on Hadoop中用到的主要技术——MPP vs Runtime Framework

    转载声明 本文转载自盘点SQL on Hadoop中用到的主要技术,个人觉得该文章对于诸如Impala这样的MPP架构的SQL引擎和Runtime Framework架构的Hive/Spark SQL ...

  2. [Node.js] querystring类

    和参数相关的帮助类,原生自带,直接 require('querystring') 即可使用. 此类一共包括4个方法: querystring.stringify(obj, [sep], [eq]) q ...

  3. 凸优化(Convex Optimization)浅析

    本博客已经迁往http://www.kemaswill.com/, 博客园这边也会继续更新, 欢迎关注~ 在机器学习中, 很多情况下我们都需要求得一个 问题的全局最优值(global optimum) ...

  4. BZOJ1822 Frozen Nova 冷冻波

    1822: [JSOI2010]Frozen Nova 冷冻波 Time Limit: 10 Sec  Memory Limit: 64 MB Description WJJ喜欢“魔兽争霸”这个游戏. ...

  5. input限制

    转载,暂未使用,可以借鉴. 出处:http://blog.csdn.net/a13590394462/article/details/73943785

  6. 【译】第四篇 Integration Services:增量加载-Updating Rows

    本篇文章是Integration Services系列的第四篇,详细内容请参考原文. 回顾增量加载记住,在SSIS增量加载有三个使用案例:1.New rows-add rows to the dest ...

  7. 在mac环境下用QT使用OpenGL,glut,glfw

    只需要在新建工程中.pro文件中添加: #opengl glut LIBS+= -framework opengl -framework glut 就可以使用glut了. 继续添加: ##glfw L ...

  8. Python time()方法

    from:http://www.runoob.com/python/att-time-time.html 描述 Python time time() 返回当前时间的时间戳(1970纪元后经过的浮点秒数 ...

  9. fcntl函数的用法总结

    fcntl系统调用可以用来对已打开的文件描述符进行各种控制操作以改变已打开文件的的各种属性 函数原型:   #include<unistd.h> #include<fcntl.h&g ...

  10. 两个Bounding Box的IOU计算代码

    Bounding Box的数据结构为(xmin,ymin,xmax,ymax) 输入:box1,box2 输出:IOU值 import numpy as np def iou(box1,box2): ...