# Dart教程 - 9 函数式编程
什么是函数式编程?
就是用函数来解决问题,函数可以赋值给变量、函数可以作为参数传递给另外一个函数、函数可以作为返回值。
# 9.1 函数作为参数
# 1 函数赋值给变量
函数可以赋值给变量,看一下下面的代码:
int compute(x, y) {
// 定义一个函数
return x + y;
}
void main() {
Function func = compute; // 将函数赋值给变量,注意函数名称后面没有括号
int result = func(1, 2); // 通过变量调用函数
print(result);
}
2
3
4
5
6
7
8
9
10
将函数赋值给变量,注意函数后面没有括号,有括号就是调用函数了,变量可以使用 Function
或 var
来定义。
函数赋值给变量后,可以通过变量调用函数。
# 2 函数作为参数
函数可以作为参数传递给另外一个函数
int plus(x, y) {
// 定义一个加法的函数
return x + y;
}
int multiply(x, y) {
// 定义一个乘法的函数
return x * y;
}
int calculate(x, y, func) {
// 第三个参数是一个函数,在该函数中调用了这个函数
return func(x, y);
}
void main(List<String> args) {
int result1 = calculate(1, 2, plus); // 传递三个参数,第三个参数是一个函数,使用第三个参数的函数来计算前两个参数
print(result1);
int result2 = calculate(1, 2, multiply);
print(result2);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
在上面的代码中,我们将两个数字和一个函数作为参数传递给了 calculate
函数,在 calculate
函数中,将两个数字作为参数,调用了作为参数的函数。
这样根据传入的函数的不同,实现了不同的逻辑,这是计算逻辑的传递,而不是数据的传递。
这样做有什么好处呢?
# 3 函数作为参数的应用
首先我们定义一个学生类:
class Student {
String name;
int chinese;
int math;
int english;
Student(this.name, this.chinese, this.math, this.english);
String toString() {
return "{name:${this.name}, chinese:${this.chinese}, math:${this.math}, english:${this.english}}";
}
}
2
3
4
5
6
7
8
9
10
11
12
我们现在有一个学生列表:
import 'Student.dart';
void main() {
// 学生列表
List<Student> allStuList = [
Student("zhangsan", 87, 48, 92),
Student("lisi", 47, 92, 71),
Student("wangwu", 58, 46, 38),
Student("zhangliu", 95, 91, 99)
];
print(allStuList);
}
2
3
4
5
6
7
8
9
10
11
12
13
现在想获取语文成绩不及格的同学、所有课程不及格的同学、所有课程都是优秀的学生,那么我们需要定义三个函数,如下:
import 'Student.dart';
// 获取语文成绩不及格的同学
List<Student> getChineseFlunkList(List<Student> allStuList) {
List<Student> stu_list = [];
for (Student stu in allStuList) {
if (stu.chinese < 60) {
stu_list.add(stu);
}
}
return stu_list;
}
// 获取所有课程不及格的同学
List<Student> getFlunkList(List<Student> allStuList) {
List<Student> stu_list = [];
for (Student stu in allStuList) {
if (stu.chinese < 60 && stu.math < 60 && stu.english < 60) {
stu_list.add(stu);
}
}
return stu_list;
}
// 获取所有课程都是优秀的学生
List<Student> getExcellentList(List<Student> allStuList) {
List<Student> stu_list = [];
for (Student stu in allStuList) {
if (stu.chinese >= 90 && stu.math >= 90 && stu.english >= 90) {
stu_list.add(stu);
}
}
return stu_list;
}
// -------------方法调用---------------------
void main() {
// 学生列表
List<Student> allStuList = [
Student("zhangsan", 87, 48, 92),
Student("lisi", 47, 92, 71),
Student("wangwu", 58, 46, 38),
Student("zhangliu", 95, 91, 99)
];
print("语文不及格的同学:");
List<Student> stuList = getChineseFlunkList(allStuList);
for (Student stu in stuList) {
print(stu);
}
print("所有成绩都不及格的同学:");
stuList = getFlunkList(allStuList);
for (Student stu in stuList) {
print(stu);
}
print("所有成绩都是优秀的同学:");
stuList = getExcellentList(allStuList);
for (Student stu in stuList) {
print(stu);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
功能是实现了,但是上面的代码有一个问题,就是代码复用性差。上面定义了3个函数 getChineseFlunkList
、getFlunkList
、getExcellentList
,只有其中的判断条件不同。 能否抽出判断条件,将判断条件作为参数传递呢?
我们先将上面三个相似的函数抽出成一个公共的函数,根据条件获取学生列表的函数:
// 根据条件获取学生列表
List<Student> getStuListByCondition(
List<Student> allStuList, Function funCondition) {
List<Student> stu_list = [];
for (Student stu in allStuList) {
if (funCondition(stu)) {
stu_list.add(stu);
}
}
return stu_list;
}
2
3
4
5
6
7
8
9
10
11
12
上面的函数第二个参数传进来的是一个函数,在 if 的判断后面执行这个函数,如果这个函数返回的是true,则将元素添加到列表中。
然后我们再编写三个获取筛选学生条件的函数,根据传进来的参数进行判断:
// 获取语文成绩不及格的同学
bool getChineseFlunkCondition(stu) {
return stu.chinese < 60;
}
// 获取所有课程不及格的同学
bool getFlunkCondition(stu) {
return stu.chinese < 60 && stu.math < 60 && stu.english < 60;
}
// 获取所有课程都是优秀的学生
bool getExcellentCondition(stu) {
return stu.chinese >= 90 && stu.math >= 90 && stu.english >= 90;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
然后我们可以根据条件获取相应的学生了,都是调用共用的函数 getStuListByCondition
, 传入不同的条件获取不同的学生列表:
print("语文不及格的同学:");
List<Student> stuList =
getStuListByCondition(allStuList, getChineseFlunkCondition);
for (Student stu in stuList) {
print(stu);
}
print("所有成绩都不及格的同学:");
stuList = getStuListByCondition(allStuList, getFlunkCondition);
for (Student stu in stuList) {
print(stu);
}
print("所有成绩都是优秀的同学:");
stuList = getStuListByCondition(allStuList, getExcellentCondition);
for (Student stu in stuList) {
print(stu);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
这样已函数作为参数进行逻辑的控制,代码更为优雅,复用性更高。
完整代码:
import 'Student.dart';
// 根据条件获取学生列表
List<Student> getStuListByCondition(
List<Student> allStuList, Function funCondition) {
List<Student> stu_list = [];
for (Student stu in allStuList) {
if (funCondition(stu)) {
stu_list.add(stu);
}
}
return stu_list;
}
// 获取语文成绩不及格的同学
bool getChineseFlunkCondition(stu) {
return stu.chinese < 60;
}
// 获取所有课程不及格的同学
bool getFlunkCondition(stu) {
return stu.chinese < 60 && stu.math < 60 && stu.english < 60;
}
// 获取所有课程都是优秀的学生
bool getExcellentCondition(stu) {
return stu.chinese >= 90 && stu.math >= 90 && stu.english >= 90;
}
// -------------方法调用---------------------
void main() {
// 学生列表
List<Student> allStuList = [
Student("zhangsan", 87, 48, 92),
Student("lisi", 47, 92, 71),
Student("wangwu", 58, 46, 38),
Student("zhangliu", 95, 91, 99)
];
print("语文不及格的同学:");
List<Student> stuList =
getStuListByCondition(allStuList, getChineseFlunkCondition);
for (Student stu in stuList) {
print(stu);
}
print("所有成绩都不及格的同学:");
stuList = getStuListByCondition(allStuList, getFlunkCondition);
for (Student stu in stuList) {
print(stu);
}
print("所有成绩都是优秀的同学:");
stuList = getStuListByCondition(allStuList, getExcellentCondition);
for (Student stu in stuList) {
print(stu);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
执行结果:
语文不及格的同学: name:lisi, chinese:47, math:92, english:71 name:wangwu, chinese:58, math:46, english:38 所有不及格的同学: name:wangwu, chinese:58, math:46, english:38 所有成绩都是优秀的同学: name:zhangliu, chinese:95, math:91, english:99
# 4 匿名函数
匿名函数是一种没有显式命名的函数。一般定义完匿名函数会将函数赋值给一个变量。
举个栗子:
void main() {
//定义了一个匿名函数
Function printMessage = (String str) {
print(str);
};
// 调用匿名函数
printMessage("Hello World!"); // 输出:Hello World!
}
2
3
4
5
6
7
8
9
上面定义了一个匿名函数,然后将函数赋值给变量 printMessage
。函数的类型是 Function
,当然也可以使用 var
来定义。
我们甚至在定义匿名函数后并调用匿名函数:
void main() {
//定义了一个匿名函数
(String str) {
print(str);
}("Hello World!");
}
2
3
4
5
6
上面定义了一个匿名函数,在后面添加 ("Hello World!")
表示调用这个函数并传递了 "Hello World!"
做为参数。通过上面可以看出,在Dart中,函数内部是可以嵌套定义函数的。
现在我们可以将前面函数作为参数的代码改成匿名表函数的方式:
import 'Student.dart';
// 根据条件获取学生列表
List<Student> getStuListByCondition(
List<Student> allStuList, Function funCondition) {
List<Student> stu_list = [];
for (Student stu in allStuList) {
if (funCondition(stu)) {
stu_list.add(stu);
}
}
return stu_list;
}
// -------------方法调用---------------------
void main() {
// 学生列表
List<Student> allStuList = [
Student("zhangsan", 87, 48, 92),
Student("lisi", 47, 92, 71),
Student("wangwu", 58, 46, 38),
Student("zhangliu", 95, 91, 99)
];
print("语文不及格的同学:");
List<Student> stuList = getStuListByCondition(allStuList, (Student stu) {
return stu.chinese < 60;
});
for (Student stu in stuList) {
print(stu);
}
print("所有成绩都不及格的同学:");
stuList = getStuListByCondition(allStuList, (stu) { // 函数的参数类型可以省略
return stu.chinese < 60 && stu.math < 60 && stu.english < 60;
});
for (Student stu in stuList) {
print(stu);
}
print("所有成绩都是优秀的同学:");
stuList = getStuListByCondition(allStuList, (stu) {
return stu.chinese >= 90 && stu.math >= 90 && stu.english >= 90;
});
for (Student stu in stuList) {
print(stu);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
# 5 lambda表达式
如果匿名函数只有一行代码,可以使用lambda表达式来写,可以更简洁。
Lambda 表达式使用箭头符号 =>
来定义函数体,可以在一行代码中定义函数逻辑。
lambda表达式的语法:
(参数列表) => 表达式
方法体不需要return语句,会自动返回。
以获取语文成绩不及格的条件函数为例,我们可以将匿名函数改写成 lambda表达式。
print("语文不及格的同学:");
List<Student> stuList = getStuListByCondition(allStuList, (stu) {
return stu.chinese < 60;
});
2
3
4
改写为:
print("语文不及格的同学:");
List<Student> stuList = getStuListByCondition(allStuList, (stu) => stu.chinese < 60);
2
注意:箭头后面不要添加 {}
将另外两个函数也改成 lambda 表达式:
print("语文不及格的同学:");
List<Student> stuList =
getStuListByCondition(allStuList, (Student stu) => stu.chinese < 60);
for (Student stu in stuList) {
print(stu);
}
print("所有成绩都不及格的同学:");
stuList = getStuListByCondition(allStuList,
(stu) => stu.chinese < 60 && stu.math < 60 && stu.english < 60);
for (Student stu in stuList) {
print(stu);
}
print("所有成绩都是优秀的同学:");
stuList = getStuListByCondition(allStuList,
(stu) => stu.chinese >= 90 && stu.math >= 90 && stu.english >= 90);
for (Student stu in stuList) {
print(stu);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
代码是不是更简洁了。
lambda就是为了将方法作为参数而存在的,当然如果你将lambda表达式赋值给一个变量,也不是不可以,只是有点没事找抽,那还不如定义成原来函数的模样。
print("语文不及格的同学:");
Function getChineseFlunkCondition = (stu) => stu.chinese < 60;
List<Student> stuList = getStuListByCondition(allStuList, getChineseFlunkCondition);
for (Student stu in stuList) {
print(stu);
}
2
3
4
5
6
7
需要注意:lambda表达式的方法体只能有一行代码,无法写多行。
完整代码:
import 'Student.dart';
// 根据条件获取学生列表
List<Student> getStuListByCondition(
List<Student> allStuList, Function funCondition) {
List<Student> stu_list = [];
for (Student stu in allStuList) {
if (funCondition(stu)) {
stu_list.add(stu);
}
}
return stu_list;
}
// -------------方法调用---------------------
void main() {
// 学生列表
List<Student> allStuList = [
Student("zhangsan", 87, 48, 92),
Student("lisi", 47, 92, 71),
Student("wangwu", 58, 46, 38),
Student("zhangliu", 95, 91, 99)
];
print("语文不及格的同学:");
List<Student> stuList =
getStuListByCondition(allStuList, (Student stu) => stu.chinese < 60);
for (Student stu in stuList) {
print(stu);
}
print("所有成绩都不及格的同学:");
stuList = getStuListByCondition(allStuList,
(stu) => stu.chinese < 60 && stu.math < 60 && stu.english < 60);
for (Student stu in stuList) {
print(stu);
}
print("所有成绩都是优秀的同学:");
stuList = getStuListByCondition(allStuList,
(stu) => stu.chinese >= 90 && stu.math >= 90 && stu.english >= 90);
for (Student stu in stuList) {
print(stu);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
# 9.2 函数作为返回值
# 1 闭包
什么是闭包?
闭包必须满足三个条件:
- 在一个外部函数中有一个内部函数
- 内部函数必须引用外部函数中的变量
- 外部函数的返回值必须是内部函数
举个栗子:
Function outer_func() {
int a = 1;
void inner_func() {
print(a); // 使用了外部函数的变量
}
return inner_func; // 将内部函数返回,这里函数名后没有括号
}
void main() {
var result = outer_func(); // 这里得到的结果是一个函数
result(); // 执行内部函数,结果为:1
}
2
3
4
5
6
7
8
9
10
11
12
13
14
在上面外部函数 outer_func
中定义了一个内部函数 inner_func
,内部函数中引用了外部函数的变量 a
,然后外部函数返回了内部函数。
下面来讲解一下闭包的实际应用。
# 2 在函数之间共享数据
使用闭包可以在内部函数和外部函数之间共享数据。
举个栗子:
Function counter() {
int count = 0;
int inner() {
count += 1;
return count;
}
return inner;
}
void main() {
var c1 = counter(); // 创建了一个计数器
print(c1()); // 输出 1
print(c1()); // 输出 2
var c2 = counter(); // 创建了第二个计数器
print(c2()); // 输出 1
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
在上面的代码中,创建了闭包,用来实现了一个计数器的功能。
当我们调用外部函数 counter
就会创建一个计数器,当我们调用这个计数器的时候,count
的值就会加1,由于我们每次调用counter
函数时都会创建一个新的闭包,因此我们可以创建多个计数器,每个计数器都是独立的。
可以看到闭包可以用于封装私有状态,没有使用全局的变量,避免变量暴露在全局作用域中,实现更好的封装和数据隐藏。
执行结果:
1
2
1
其实,我们也可以创建一个计数器的类,在类中定义一个count属性,也可以实现上面的功能,例如:
class Counter {
int count = 0;
int increment() {
this.count += 1;
return this.count;
}
}
void main() {
// 创建计数器
var counter1 = Counter();
var counter2 = Counter();
print(counter1.increment()); // 输出 1
print(counter2.increment()); // 输出 1
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
一般情况下,类实现方式更适合需要维护的数据结构较为复杂的情况,比如需要使用多个属性和方法来处理数据。而闭包实现方式更适合只需要共享一些简单数据的情况。
# 3 作为回调函数
回调函数指的是将一个函数作为参数传递给另外一个函数,然后在这个函数中执行这个参数函数。回调函数通常用于异步编程中,可以在事件发生后回调执行,以完成一些特定的任务。例如:在实际的功能实现中,我们经常会做一些耗时的操作,例如网络请求,发起网络请求后,继续执行后面的代码,待服务器返回结果后,在通过回调函数的方式返回结果。
这里举一个回调函数的列子:
void plus(a, b, callback) {
// callback参数就是一个函数
int result = a + b;
callback(result); // 执行callback函数将结果返回
}
void main() {
plus(10, 20, (int result) {
print("计算结果:$result");
}); // 计算结果:30
}
2
3
4
5
6
7
8
9
10
11
调用 plus
函数的时候,传入了一个函数,得到计算结果后,调用传入的函数,并传递结果。
# 9.3 Function 和 Function()
我们在前面定义函数,可以将函数赋值给 Function 类型:
举个栗子:
int helloDoubi(x, y) {
return x + y;
}
int helloShabi() {
return 0;
}
void main() {
Function func = helloDoubi; // 将helloDoubi函数赋值给变量func
func = helloShabi; // 也可以将helloShabi函数赋值给变量func
}
2
3
4
5
6
7
8
9
10
11
12
13
那么 Function() 表示什么意思呢?
**Function()
表示一个没有参数的函数。**不是创建一个函数对象。
举个栗子,修改一下上面的代码,将 Function
修改为 Function()
:
int helloDoubi(x, y) {
return x + y;
}
int helloShabi() {
return 0;
}
void main() {
Function() func = helloDoubi; // 报错,因为helloDoubi函数是有参数的
func = helloShabi; // 可以,因为helloShabi函数是没有参数的
}
2
3
4
5
6
7
8
9
10
11
12
13
代码 Function() func = helloDoubi;
是会报错的,因为 Function() func
表示定义了没有参数的函数,而 helloDoubi
函数是有参数的,所以无法赋值给 func
变量,而 helloShabi
函数没有参数,可以赋值给 func
;
我们定义函数变量的时候,还可以指定函数的参数
int helloDoubi(x, y) {
return x + y;
}
int helloShabi() {
return 0;
}
void main() {
Function(int, int) func = helloDoubi; // 可以,因为helloDoubi函数是有参数的
func = helloShabi; // 报错,func接收两个整形的参数
}
2
3
4
5
6
7
8
9
10
11
12
在上面的代码中,定义了 func
接收两个整形的参数,那么可以将 helloDoubi
函数赋值给 func
,但是 helloShabi
函数不行。
还可以使用可选参数进行定义:
int helloDoubi(x, {String name=""}) {
return x + name;
}
Function(int, {String name}) func = helloDoubi;
2
3
4
5