Cris 的 Scala 笔记(六):函数式编程基础

6. 函数式编程基础(重点)

- 函数式编程和 oop 的关系

- 概念说明
  1. 在scala中,方法和函数几乎可以等同(比如他们的定义、使用、运行机制都一样的),只是函数的使用方式更加的灵活多样。

2)) 函数式编程是从编程方式(范式)的角度来谈的,可以这样理解函数式编程把函数当做一等公民,充分利用函数支持的函数的多种使用方式

​ 在Scala当中,函数是一等公民,像变量一样,既可以作为函数的参数使用,也可以将函数赋值给一个变量. ,函数的创建不用依赖于类或者对象,而在Java当中,函数的创建则要依赖于类、抽象类或者接口

  1. 面向对象编程是以对象为基础的编程方式。

  2. 在 Scala 中函数式编程和面向对象编程融合在一起了

- 函数式编程介绍

Ø “函数式编程"是一种"编程范式”(programming paradigm)。

Ø 它属于"结构化编程"的一种,主要思想是把运算过程尽量写成一系列嵌套的函数调用。

Ø 函数式编程中,将函数也当做数据类型,因此可以接受函数当作输入(参数)和输出(返回值)。

Ø 函数式编程中,最重要的就是函数。

- 函数式编程基本语法

小练习

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

    val res = getResult(1, 2, '=')
    if (res != null) println("result=" + res) else println("返回结果不正确!请输入规定的操作符")
  }

  /**
    * 要求:定义一个方法,只对 + 和 - 做出对应的操作
    */
  def getResult(a: Int, b: Int, x: Char) = {
    if (x == '+') {
      a + b
    } else if (x == '-') {
      a - b
    } else {
      null
    }
  }

}
- 递归(重点)
  1. 执行一个函数时,就创建一个新的受保护的独立空间(新函数栈)

  2. 函数的局部变量是独立的,不会相互影响

  3. 递归必须向退出递归的条件逼近,否则就是无限递归,死龟了:)

  4. 当一个函数执行完毕,或者遇到return,就会返回,遵守谁调用,就将结果返回给谁,同时当函数执行完毕或者返回时,该函数栈本身也会被系统销毁

递归练习

  1. 以下两个递归函数的结果

      def test(n: Int): Unit = {
        if (n > 2) {
          test(n - 1)
        }
        println("n=" + n)
      }
    
      def test2(n: Int): Unit = {
        if (n > 2) {
          test(n - 2)
        } else {
          println("n=" + n)
        }
      }
    
  2. 请使用递归的方式,求出斐波那契数1,1,2,3,5,8,13… 给你一个位置n,求出它的斐波那契数是多少?

    def feibo(n: Int): Int = {
      if (n <= 0) {
        0
      } else {
        if (n <= 2) 1 else feibo(n - 2) + feibo(n - 1)
      }
    }
    
  3. 已知 f(1)=3; f(n) = 2*f(n-1)+1; 请使用递归的思想编程,求出 f(n)的值?、

    //已知 f(1)=3; f(n) = 2*f(n-1)+1; 请使用递归的思想编程,求出 f(n)的值?
    def func1(n: Int): Int = {
      if (n < 1) {
        0
      } else {
        if (n == 1) 1 else 2 * func1(n - 1) + 1
      }
    }
    
  4. 有一堆桃子,猴子第一天吃了其中的一半,并再多吃了一个!以后每天猴子都吃其中的一半,然后再多吃一个。当到第十天时,想再吃时(还没吃),发现只有1个桃子了。问题:最初共多少个桃子

    def func2(n: Int): Int = {
      if (n == 10) {
        1
      } else {
        2 * (func2(n + 1) + 1)
      }
    }
    
- 函数细节(重要!)
  1. 函数的形参列表可以是多个,函数没有形参,调用时 可以不带()

  2. 形参列表和返回值列表的数据类型可以是值类型和引用类型

  3. Scala中的函数可以根据函数体最后一行代码自行推断函数返回值类型。那么在这种情况下,return关键字可以省略

  4. 因为Scala可以自行推断,所以在省略return关键字的场合,返回值类型也可以省略

  5. 如果函数明确使用return关键字,那么函数返回就不能使用自行推断了,这时要明确写成 : 返回类型 = ,当然如果你什么都不写,即使有return 关键字返回值也为()

/*返回值为 ()*/
def test2(): Unit = {

}

/*确定返回值类型*/
def test3(): Int = {
  1
}

/*自动对返回值进行类型推断*/
def test4(n: Boolean) = {
  if (n) "cris" else 4
}
  1. 如果函数明确声明无返回值(声明Unit),那么函数体中即使使用return关键字也不会有返回值

  2. 如果明确函数无返回值或不确定返回值类型,那么返回值类型可以省略(或声明为Any)

  3. Scala语法中任何的语法结构都可以嵌套其他语法结构,即:函数中可以再声明/定义函数,类中可以再声明类 ,方法中可以再声明/定义方法

  4. Scala函数的形参,在声明参数时,直接赋初始值(默认值),这时调用函数时,如果没有指定实参,则会使用默认值。如果指定了实参,则实参会覆盖默认值

def testAge(age: Int = 12): Unit = {
  println(age)
}
  1. 如果函数存在多个参数,每一个参数都可以设定默认值,那么这个时候,传递的参数到底是覆盖默认值,还是赋值给没有默认值的参数,就不确定了(默认按照声明顺序[从左到右])。在这种情况下,可以采用带名参数

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

  3. 递归函数未执行之前是无法推断出来结果类型,在使用时必须有明确的返回值类型

  4. Scala函数支持可变参数

// 调用
tests(1,2,3)
// 定义
def tests(args: Int*): Unit = {
  println(args) // WrappedArray(1, 2, 3)
  for (x <- args) {
    println(x)
  }
}
  1. 函数的简写
/*func 就是 func0 的简写*/
def func = "cris"

def func0() = {
  "cris"
}

// 无参数的函数被调用可以省略小括号
println(func) // cris

var flag = true

def fuc = if (flag) "cris" else 12

println(fuc) // cris
- 过程

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

注意事项

  1. 注意区分:如果函数声明时没有返回值类型,但是有 = 号,表示可以通过类型推断最后一行代码。这个时候,函数其实是有返回值的,但是该函数并不是过程

  2. IDEA 会对没有返回值的函数自动不全,即自动加上 Unit 关键字,但是考虑到 Scala 语言的简洁性,建议不加

    // 这样的函数称之为过程,即通过 Unit 关键字表明了没有返回值
    def testfunc(): Unit ={
      
    }
    
    // 实际开发中,或者 Scala 源码中经常省略 Unit ,并使用 = 来进行类型推断该函数
    def testfunc() = {
    
    }
    
  3. 总结:定义函数的时候,如果确定返回值类型,那么使用 :返回值类型 = ,否则使用 = 即可(没有返回值的话还是建议使用 :Unit ,毕竟可读性更好)

- 惰性函数

应用场景

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

简介

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

示例

object LazyTest {
  def main(args: Array[String]): Unit = {
    
    val result = func(1, 2)
    println("-----------------")
//    println(result)
  }

  def func(a: Int, b: Int): Int = {
    println("func 函数被执行了....")
    a + b
  }

}

如果我们不加上 lazy 关键字,那么即使没有使用到 result ,函数也会在我们调用的时候去执行,如果加上 lazy 关键字呢?

    lazy val result = func(1, 2)
    println("-----------------")
//    println(result)

注意细节

  1. lazy 不能修饰 var 类型的变量

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

- 异常

异常简介

  1. Scala提供try和catch块来处理异常。try块用于包含可能出错的代码。catch块用于处理try块中发生的异常。可以根据需要在程序中有任意数量的try…catch块。

  2. Scala 语法处理上和Java类似,但是又不尽相同

Java异常处理的注意点

  1. java语言按照try—catch—finally的方式来处理异常

  2. 不管有没有异常捕获,都会执行finally, 因此通常可以在finally代码块中释放资源

3)) 可以有多个catch,分别捕获对应的异常,这时需要把范围小的异常类写在前面,把范围大的异常类写在后面,否则编译错误。会提示 “Exception ‘java.lang.xxxxxx’ has already been caught”

Scala 的异常处理

object ExceptionTest {
  def main(args: Array[String]): Unit = {
    try {
      val a = 1 / 0
    } catch {
      case ex: ArithmeticException =>
        println("发生了算数异常!")
        println("要怎么处理呢?")
      case ex: Exception => println("不用处理了~")
    } finally {
      println("finally!")
    }
  }
}

  1. 我们将可疑代码封装在try块中。 在try块之后使用了一个catch处理程序来捕获异常。如果发生任何异常,catch处理程序将处理它,程序将不会异常终止。

  2. Scala的异常的工作机制和Java一样,但是Scala没有“checked(编译期)”异常,即Scala没有编译异常这个概念,异常都是在运行的时候捕获处理。

  3. 用throw关键字,抛出一个异常对象。所有异常都是Throwable的子类型throw表达式是有类型的,就是Nothing,因为Nothing是所有类型的子类型,所以throw表达式可以用在需要类型的地方

  4. 在Scala里,借用了模式匹配的思想来做异常的匹配,因此,在catch的代码里,是一系列case子句来匹配异常,当匹配上后 => 有多条语句可以换行写,类似 java 的 switch case x: 代码块…

  5. 异常捕捉的机制与其他语言中一样,如果有异常发生,catch子句是按次序捕捉的。因此,在catch子句中,越具体的异常越要靠前,越普遍的异常越靠后,如果把越普遍的异常写在前,把具体的异常写在后,在scala中也不会报错,但这样是非常不好的编程风格

  6. finally子句用于执行不管是正常处理还是有异常发生时都需要执行的步骤,一般用于对象的清理工作,这点和Java一样

  7. Scala提供了throws关键字来声明异常。可以使用方法定义声明异常。 它向调用者函数提供了此方法可能引发此异常的信息。 它有助于调用函数处理并将该代码包含在try-catch块中,以避免程序异常终止。在scala中,可以使用throws注释来声明异常

@throws(classOf[ArithmeticException])
def func() = {
  val a = 1 / 0
}
- 函数练习
  1. 打印金字塔

    def main(args: Array[String]): Unit = {
      func1()
    }
    
    def func1(): Unit = {
      println("请输入打印的行数:")
      val i = StdIn.readInt()
      for (x <- 0.until(i)) {
        for (a <- x.until(i - 1)) {
          print(" ")
        }
        for (b <- 0.until(2 * x + 1)) {
          print("*")
        }
        println()
      }
    }
    

  2. 根据输入的函数打印九九乘法表

    def main(args: Array[String]): Unit = {
      func2()
    }
    
    def func2(): Unit = {
      println("请输入打印的乘法表行数:")
      val x = StdIn.readInt()
      for (i <- 1.to(x); y <- 1.to(i)) {
        printf("%d*%d=%d\t", y, i, y * i)
        if (i == y) {
          println()
        }
      }
    }
    

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值