函数式编程基础

  • 函数定义/声明
  • 函数运行机制
  • 递归//难点 [最短路径,邮差问题,迷宫问题, 回溯]
  • 过程
  • 惰性函数和异常

函数式编程高级

  • 值函数(函数字面量)
  • 高阶函数
  • 闭包
  • 应用函数
  • 柯里化函数,抽象控制...

函数式编程定义

注释: 我想写函数点

一.什么是函数式编程?

函数式编程思想来源于伟大数学家阿隆佐设计的lambda验算,是指用函数来解决与计算相关的几乎所有问题。

和面向对象编程以对象为模块的思想一样,函数式编程是以函数为核心来组织模块的,这种组织方式更有利于写出模块化的代码。

二.函数式编程的基本准则

1.函数是"一等公民"

所谓一等公民是指,函数与我们平时所使用的其他数据类型地位一样:

(1)可以赋值给一个变量

(2)可以作为参数进行传递

(3)可以作为别的函数的返回值

(4)函数的创建不用依赖于类或者对象,而是和对象同级别,或者说函数本身就是一种对象。(python 就是这样)

在学习Scala中将方法、函数、函数式编程和面向对象编程明确一下:

2.尽量写"纯函数"

所谓纯函数是指,给定相同输入总能得到相同输出的函数。纯函数需要同时满足下面两个条件:

(1)函数的结果只依赖于输入的参数且与外部变量和环境无关——只要输入相同,返回值总是不变的。

(2)除了返回值外,不修改程序的外部状态(比如全局变量、入参)——FP中所有的变量都是final的,这样设计的原因是因为lambda验算只关心计算的结果而不关心每个状态的值。

总之,纯函数,就是指这样一个函数,对于相同的输入,永远得到相同的输出,它不依赖外部环境,也不会改变外部环境。如果不满足以上几个条件那就是非纯函数。

 

scala函数

scala函数的定义

基本语法



def 函数名 ([参数名: 参数类型], ...)[[: 返回值类型] =] {
语句...
return 返回值
}


  1. Scala语法中任何的语法结构都可以嵌套其他语法结构(灵活),即:函数中可以再声明/定义函数,类中可以再声明类 ,方法中可以再声明/定义方法
  2. 函数声明关键字为def (definition)
  3. [参数名: 参数类型], ...:表示函数的输入(就是参数列表), 可以没有。 如果有,多个参数使用逗号间隔
  4. 函数中的语句:表示为了实现某一功能代码块
  5. 函数可以有返回值,也可以没有
  • 返回值形式1: :T= 返回值类型 是T
  • 返回值形式2: 直接 = 表示返回值类型不确定,使用类型推导完成
  • 返回值形式3: Unit表示没有返回值,即使有return也 不生效
  • 如果没有return ,默认以执行到最后一行的结果作为返回值

函数返回值:

  1. 返回值列表的数据类型可以是值类型和引用类型。
  2. Scala中的函数可以根据函数体最后一行代码自行推断函数返回值类型。那么在这种情况下,return关键字可以省略
  3. 因为Scala可以自行推断,所以在省略return关键字的场合,返回值类型也可以省略
  4. 如果函数明确使用return关键字,那么函数返回就不能使用自行推断了,这时要明确写成 : 返回类型 = ,当然如果你什么都不写,即使有return 返回值为()
  5. 如果函数明确声明无返回值(声明Unit),那么函数体中即使使用return关键字也不会有返回值
  6. 如果明确函数无返回值或不确定返回值类型,那么返回值类型可以省略(或声明为Any)
  7. 递归函数未执行之前是无法推断出来结果类型,在使用时必须有明确的返回值类型

案例参数:



  def fun1(i: Int,j: Int): Int = {
return 1
}
def fun2(i: Int,j: Int): Int = {
1
}
//自动推导
def fun3(i:Int)={
1
}
def fun4(i:Int):Unit={
return 1 //依旧返回()
}


 Scala核心编程_第05章_函数式编程_数据类型

函数传参:

  1. 函数的形参列表可以是多个, 如果函数没有形参,调用时可以不带()
  2. 形参列表的数据类型可以是值类型和引用类型。
  3. 默认传参:Scala函数的形参,在声明参数时,直接赋初始值(默认值),这时调用函数时,如果没有指定实参,则会使用默认值。如果指定了实参,则实参会覆盖默认值。
  4. 如果函数存在多个参数,每一个参数都可以设定默认值,那么这个时候,传递的参数到底是覆盖默认值,还是赋值给没有默认值的参数,就不确定了(默认按照声明顺序[从左到右])。在这种情况下,可以采用带名参数 
  5. 不定长传参:Scala函数支持可变参数的不定长传参,这个 参数 是个集合, 通过 for循环 可以访问到各个值。
  6. 默认传参和不定长传参不可以混用,否则报错;
  7. scala 函数的形参默认是val的,因此不能在函数中进行修改.

Scala核心编程_第05章_函数式编程_scala_02

函数的调用

函数的调用机制

 Scala核心编程_第05章_函数式编程_值类型_03

函数的递归调用

 Scala核心编程_第05章_函数式编程_数据类型_04

过程

基本介绍

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

案例说明:

Scala核心编程_第05章_函数式编程_scala_05

注意:

注意区分: 如果函数声明时没有返回值类型,但是有 = 号,可以进行类型推断最后一行代码。这时这个函数实际是有返回值的,该函数并不是过程。(这点在讲解函数细节的时候讲过的.)

开发工具的自动代码补全功能,虽然会自动加上Unit,但是考虑到Scala语言的简单,灵活,最好不加.

惰性函数

惰性计算

惰性计算(尽可能延迟表达式求值)是许多函数式编程语言的特性。惰性集合在需要时提供其元素,无需预先计算它们,这带来了一些好处。

  1. 可以将耗时的计算推迟到绝对需要的时候。
  2. 可以创造无限个集合,只要它们继续收到请求,就会继续提供元素。

函数的惰性使用能够得到更高效的代码。Java 并没有为惰性提供原生支持,Scala提供了。

介绍

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



object HelloScala {
def main(args: Array[String]): Unit = {
//惰性加载 这里并没有直接sum()
lazy val res = sum(10, 20)
println("-----------------")
println("res=" + res) //在要使用res 前,才执行 }

}


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

}


Scala核心编程_第05章_函数式编程_scala_06

注意:

lazy 不能修饰 var 类型的变量

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

 


大多数人都以为是才智成就了科学家,他们错了,是品格。---爱因斯坦