9,scala 函数式编程 ( 基础 ) : 递归,函数,方法,内存图,可变参数,过程,惰性函数,异常处理
程序员文章站
2024-02-22 21:22:10
...
1 ,面向对象编程,函数式编程 :
- 面向对象 : 万物皆对象。
- 函数式编程 : 函数是可以定义的,也可以被当做参数传递的,也可以被赋值给变量。
2 ,方法与函数 :method - function
- 很相似
- 几乎一样
- 方法可以转换成函数
3 ,自定义方法 :
def sum(a: Int, b: Int): Int = {
a+b
}
4 ,方法转函数 :
var s = sum _
println(s(1,2))
5 ,自定义函数 :
def main(args: Array[String]): Unit = {
val sum = (a:Int,b:Int) =>{
a+b
}
println(sum(1,2))
}
6 ,函数更灵活
7 ,函数式编程 :
- 把函数当做根本的编程单位
- 函数也是一个对象
- 函数是一等公民
- 充分利用函数
- 支持函数的多种使用方式
- 函数可以作为参数传递给另一个函数
- 函数也可以作为值,传递给一个变量
8 ,函数运行过程 : 内存分析
- 栈 : 压栈执行,先进后出
- 栈 : 是一种数据结构
- 运行 : 不停的压栈出栈
- 内存图 :压栈执行,栈柄移动
9 ,递归 : 入门 ( 从里到外 )
- 代码 :
object Aa2 {
def main(args: Array[String]): Unit = {
test(4)
}
def test(a:Int): Unit ={
if(a>2){
test(a-1)
}
println(a)
}
}
- 结果 :
2
3
4
- 压栈执行内存图 : 递归是最适合于理解内存图的算法了
10 ,递归的约束 : 不要成为死龟
递归算法,必须向着结束递归的方向逼近,不然就成为无限递归,也就是死龟。
11 ,递归练习 : 斐波那契数
- 数列 : 1,1,2,3,5,8,13
- 规律 : a3 = a2 + a1
- 求 : an = ?
- 代码 :
package com.heima.day01
object Aa2 {
def main(args: Array[String]): Unit = {
println(fbnq(1))
println(fbnq(2))
println(fbnq(3))
println(fbnq(4))
println(fbnq(5))
println(fbnq(6))
println(fbnq(7))
}
def fbnq(a:Int): Int ={
if(a==1){
return 1
}else if(a==2){
return 1
}else{
return fbnq(a-1)+fbnq(a-2)
}
}
}
12 ,递归练习 : 求函数值
- 已知 :
f(1)=3;
f(n) = 2*f(n-1)+1; - 求 : f(n)
- 代码 :
package com.heima.day01
object Aa3 {
def main(args: Array[String]): Unit = {
println(test(1))
println(test(2))
println(test(3))
println(test(4))
println(test(5))
}
def test(a: Int): Int = {
if(a==1){
return 3;
}else{
2 * test(a-1)+1;
}
}
}
13 ,递归练习 : 猴子吃桃子问题
- 题设 :
有一堆桃子,猴子第一天吃了其中的一半,并再多吃了一个!以后每天猴子都吃其中的一半,然后再多吃一个。当到第十天时,想再吃时(还没吃),发现只有1个桃子了。问题:最初共多少个桃子? - 代码 :
package com.heima.day01
object Aa4 {
def main(args: Array[String]): Unit = {
println(test(1))
}
def test(a:Int): Int ={
if(a==10){
1
}else if(a==9){
(1+1)*2
}else{
(test(a+1)+1)*2
}
}
}
14 ,函数使用总结 :
- 函数的形参列表可以是多个, 如果函数没有形参,调用时 可以不带()
- 形参列表和返回值列表的数据类型可以是值类型和引用类型。
- Scala中的函数可以根据函数体最后一行代码自行推断函数返回值类型。那么在这种情况下,return关键字可以省略
- 因为Scala可以自行推断,所以在省略return关键字的场合,返回值类型也可以省略
- 如果函数明确使用return关键字,那么函数返回就不能使用自行推断了,这时要明确写成 : 返回类型 = ,当然如果你什么都不写,即使有return 返回值为()
- 如果函数明确声明无返回值(声明Unit),那么函数体中即使使用return关键字也不会有返回值
- 如果明确函数无返回值或不确定返回值类型,那么返回值类型可以省略(或声明为Any)
- Scala语法中任何的语法结构都可以嵌套其他语法结构(灵活),即:函数中可以再声明/定义函数,类中可以再声明类 ,方法中可以再声明/定义方法
- Scala函数的形参,在声明参数时,直接赋初始值(默认值),这时调用函数时,如果没有指定实参,则会使用默认值。如果指定了实参,则实参会覆盖默认值。
例如这样 :
def sayOk(name : String = "jack"): String = {
return name + " ok! "
}
- scala 函数的形参默认是val的,因此不能在函数中进行修改。
- 递归函数未执行之前是无法推断出来结果类型,在使用时必须有明确的返回值类型。
- Scala函数支持可变参数
1 ,args 是集合, 通过 for循环 可以访问到各个值。
2 ,可变参数需要写在形参列表的最后。
3 ,例子 :
//支持0到多个参数
def sum(args :Int*) : Int = {
}
//支持1到多个参数
def sum(n1: Int, args: Int*) : Int = {
}
15 ,默认参数,带名参数 :
- 不带名 : 默认从左到右给参数赋值
- 带名 : 给指定参数赋值
object Aa4 {
def main(args: Array[String]): Unit = {
test(b=3)
}
def test(a: Int = 1, b: Int = 2): Unit = {
println(a+","+b)
}
}
16 ,过程 :procedure
没有返回值的函数
17 ,惰性函数 : 节省内存开销
- 目的 : 不到最后一刻不开工
1 ,如果一个计算分很多步骤。
2 ,将必须做的事情给做了。
3 ,不必要的事情先不做。
4 ,用到的时候再去做。 - 代码 :
object Aa4 {
def main(args: Array[String]): Unit = {
lazy val res = test(1,2)
println("11111111")
println(res)
println("22222222")
}
def test(a: Int = 1, b: Int = 2): Int = {
println("开工啦")
a+b
}
}
- 结果 :
11111111
开工啦
3
22222222
18 ,异常 : 捕获异常
- scala 异常 : 只有一种异常,运行时异常
- 代码 :
def main(args: Array[String]): Unit = {
try {
val r = 10 / 0
} catch {
case ex: ArithmeticException=> println("捕获了除数为零的算数异常")
case ex: Exception => println("捕获了异常")
} finally {
// 最终要执行的代码
println("scala finally...")
}
}
18 ,异常 : 自定义异常
object Aa4 {
def main(args: Array[String]): Unit = {
var t = test()
}
def test(): Nothing = {
throw new Exception("自定义异常")
}
}
19 ,练习 : 打印金字塔 :
- 代码 :
object Aa4 {
def main(args: Array[String]): Unit = {
for (i <- 1 to 9; if i % 2 != 0) {
print(" " * ((9 - i) / 2))
for (j <- 1 to i) {
print("*")
}
println()
}
}
}
- 效果 :
20 ,练习 : 打印九九惩乘法表
- 代码 :
object Aa4 {
def main(args: Array[String]): Unit = {
for (i <- 1 to 9; j<- 1 to 9 ) {
print(j + " x " + i + " = " + i*j + "\t")
if(j==i){
println()
}
}
}
}
- 效果 :