0701-Scala函数式编程
第一章 函数式编程
1.1 问题引入
有数组numberList = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],编写程序完成以下目标:
- 1.1 将numberList中的每个元素加1得到一个新的数组
- 1.2 将numberList中的每个元素乘2得到一个新的数组
- 1.3 将numberList中的每个元素模3得到一个新的数组
1.2 实现
# 1.1 将numberList中的每个元素加1得到一个新的数组
def func1(numberList){
newList = []
for num in numberList:
newList.append(num + 1)
}
# 1.2 将numberList中的每个元素乘2得到一个新的数组
def func2(numberList){
newList = []
for num in numberList:
newList.append(num * 2)
}
# 1.3 将numberList中的每个元素模3得到一个新的数组
def func3(numberList){
newList = []
for num in numberList:
newList.append(num % 3)
}
# 调用函数
# 1.1 将numberList中的每个元素加1得到一个新的数组
func1(numberList)
# 1.2 将numberList中的每个元素乘2得到一个新的数组
func2(numberList)
# 1.3 将numberList中的每个元素模3得到一个新的数组
func3(numberList)
1.3 使用函数式编程进行改进
# 高阶函数`map`
# 该函数接受一个函数和一个数组作为输入,函数体中将这个函数作用于数组的每个元素然后作为返回值返回
def map(mappingFuction, numberList):
newList = []
for num in numberList:
newList.append(mappingFuction(num))
# 调用函数
# 1.1 将numberList中的每个元素加1得到一个新的数组
map(lambda x: x + 1, numberList)
# 1.2 将numberList中的每个元素乘2得到一个新的数组
map(lambda x: x * 2, numberList)
# 1.3 将numberList中的每个元素模3得到一个新的数组
map(lambda x: x % 3, numberList)
1.4 什么是函数式编程
第二章 Scala函数式编程基础语法
2.1 函数声明
函数与方法:
java中的方法和Scala中的函数都可以进行功能的封装,但是方法必须和类型(类)绑定,但是函数不需要
def 函数名(参数名[:参数类型]…)[: 返回值类型 = ] {函数体}
- 无参, 无返回值
// 无参 ,无返回值
def test1(): Unit = {
println("无参 ,无返回值")
}
- 有参, 无返回值
// 有参, 无返回值
// Scala中没有重载的概念,如果在同一个作用域中,函数不能重名
def test2(s: String): Unit = {
println(s)
}
- 有参, 有返回值
// 有参,有返回值
def test3(s: String): String = {
return s + "--"
}
- 无参, 有返回值
// 无参,有返回值
def test4(): String = {
return "Hello World"
}
2.2 自动推断, 能省则省
- 如果函数声明时,明确无返回值 Unit,那么即使函数体中有return也不起作用
def test1(): Unit = {
return "zhangsan"
}
- 如果将函数体的最后一行代码进行返回,那么return关键字可以省略
// 2.如果将函数体的最后一行代码进行返回,那么return关键字可以省略
def test2(): String = {
"zhangsan"
}
- 如果可以根据函数体的最后一行代码推断出类型,那么函数的返回值类型可以省略
// 3.如果可以根据函数体的最后一行代码推断出类型,那么函数的返回值类型可以省略
def test3() = {
"zhangsan"
}
- 如果整个函数体只有一行代码,那么大括号可以省略
def test4() = "zhangsan"
- 如果函数声明中没有参数列表,那么小括号可以省略
def test5 = "zhangsan"
如果函数声明时省略小括号,那么调用函数时不能增加括号
- 如果明确函数没有返回值,那么 = 可以省略,最后一行代码也不会被返回
def test6() {
"zhangsan"
}
如果函数没有参数列表,可以
省略小括号,调用时一定不能加
如果函数没有参数列表,但是没有省略小括号,调用时 可加可不加
2.3 可变参数
// TODO 可变参数
def test1(name: String*) = {
print(name)
}
java中如何实现可变参数
String...
public static void printMax( double... numbers) {
if (numbers.length == 0) {
System.out.println("No argument passed");
return;
}
double result = numbers[0];
for (int i = 1; i < numbers.length; i++){
if (numbers[i] > result) {
result = numbers[i];
}
}
System.out.println("The max value is " + result);
}
2.4 默认参数
def test2(name: String, age: Int = 20): Unit = {
print(name, age)
}
test2("zhangsan")
java中实现默认参数:
方法重载
public static void main(String[] args) throws ParseException {
// 日期格式化
Date d = parse("2019-12-16 12:00:00", "yyyy-MM-dd HH:mm:ss");
}
public static Date parse(String date, String format) throws ParseException {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat();
return simpleDateFormat.parse(date);
}
// 将format设为默认参数
public static void main(String[] args) throws ParseException {
// 日期格式化
//Date d = parse("2019-12-16 12:00:00", "yyyy-MM-dd HH:mm:ss");
// 将format设为默认参数
Date parse = parse("2019-12-16 12:00:00");
}
public static Date parse(String date) throws ParseException {
return parse(date, "yyyy-MM-dd HH:mm:ss");
}
private static Date parse(String date, String format) throws ParseException {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat();
return simpleDateFormat.parse(date);
}
第三章 Scala高阶函数
3.1 函数作为参数
将函数作为参数传递给另外一个函数,需要采用特殊的声明方式
函数名:参数列表=>返回值类型
可以理解为变量名 : 变量类型
- 定义函数, 指定函数作为参数
// 高阶函数, 函数做参数, f是形参
def f4(f: (Int) => Int) = {
f(4) + 10
}
// 普通函数
def f5(i: Int) = {
i * 3
}
- 调用函数
// 调用f4, 将f5作为参数传递给f4, f5 是实参
val result: Int = f4(f5)
println(result)
3.2 匿名函数
3.1 的写法太麻烦, 还需要再重新定义 f5, 使用匿名函数, 不必再定义f5
// 定义高阶函数
def f6(f: (Int) => Int): Int = {
f(4) + 10
}
// 调用
val result2: Int = f6(x => x * 3)
println(result2)
3.3 函数作为返回值
def f() = {
println("function")
}
// TODO 函数作返回值
def f0() = {
// 返回函数本身,而不是返回函数的执行结果,但是,因为函数f是无参的,所以就算这里不加括号f()也是在调用f执行
// f
// 直接返回有问题,加特殊符号
f _
}
f0()()
3.4 闭包
// TODO 闭包
// 一个函数在实现时,将外部变量引入到函数的内容,改 变了这个变量的生命周期,称之为闭包
def f1(i: Int) = {
def f2(j: Int) = {
i * j
}
f2 _
}
f1(2)(3)
3.5 柯里化
def f3(i: Int)(j: Int) = {
i * j
}
f3(2)(3)
函数编程中,接受
多个参数的函数都可以转化为接受单个参数的函数
,这个转化过程就叫柯里化,柯里化就是证明了函数只需要一个参数而已。其实我们刚才的学习过程中,已经涉及到了柯里化操作,所以这也印证了,柯里化就是以函数为主体这种思想发展的必然产生的结果。
- 柯里化实例
def mul(x: Int, y: Int) = x * y
println(mul(10, 10))
def mulCurry(x: Int) = (y: Int) => x * y
println(mulCurry(10)(9))
def mulCurry2(x: Int)(y:Int) = x * y
println(mulCurry2(10)(8))
3.6 匿名函数的省略
// TODO 省略的过程
def f7(f: (Int, Int) => Int) = {
f(10, 10)
}
// 原型
f7((x: Int, y: Int) => {x + y})
// 省略参数类型,自动推断
f7((x, y) => {x + y})
// 函数体只有一行代码,省略大括号
f7((x, y) => x + y)
// 参数只被使用了一次,用_代替
f7(_ + _)
f8((i: Int )=> {println(i)})
f8((i)=> {println(i)})
f8((i)=> println(i))
f8((println(_)))
f8(println
第四章 Java中的函数式编程
4.1 问题引入
List<Employee> employees = Arrays.asList(
new Employee("zhangsan",38,2999),
new Employee("lisisddd",28,2399),
new Employee("wangwuid",48,4599),
new Employee("zhansdsd",58,6599),
new Employee("zsdngsan",28,2399),
new Employee("sddngsan",18,3199)
);
// 筛选年龄大于35岁的所有员工
public List<Employee> filterByAge(List<Employee> employees) {
List<Employee> emp = new ArrayList<>();
for (Employee employee : employees) {
if (employee.getAge() > 35) {
emp.add(employee);
}
}
return emp;
}
// 筛选年龄工资大于3000的所有员工
public List<Employee> filterBySalary(List<Employee> employees) {
List<Employee> emp = new ArrayList<>();
for (Employee employee : employees) {
if (employee.getSalary() > 3000) {
emp.add(employee);
}
}
return emp;
}
@Test
public void test1() {
// 按照年龄过滤
List<Employee> employees = filterByAge(this.employees);
for (Employee employee : employees) {
System.out.println(employee);
}
// 按照工资过滤
List<Employee> employees2 = filterBySalary(this.employees);
for (Employee employee : employees2) {
System.out.println(employee);
}
}
4.2 使用策略设计模式进行优化
4.2.1 定义策略接口
package com.lz.java8.lambda;
public interface MyFilter {
public boolean test(Employee employee);
}
4.2.2 定义不同的策略
- 基于年龄过滤的策略
package com.lz.java8.lambda;
/**
* @ClassName MyFilterByAge
* @Description: TODO
* @Author MAlone
* @Date 2019/12/16
* @Version V1.0
**/
public class MyFilterByAge implements MyFilter {
@Override
public boolean test(Employee employee) {
return employee.getAge() > 35;
}
}
- 基于工资过滤的策略
package com.lz.java8.lambda;
/**
* @ClassName MyFilterBySalary
* @Description: TODO
* @Author MAlone
* @Date 2019/12/16
* @Version V1.0
**/
public class MyFilterBySalary implements MyFilter {
@Override
public boolean test(Employee employee) {
return employee.getSalary() > 3000;
}
}
4.2.3 设计基于策略的业务逻辑
// 优化方式一: 策略设计模式
public List<Employee> filterEmployee(List<Employee> employees, MyFilter filter) {
List<Employee> emp = new ArrayList<>();
for (Employee employee : employees) {
if (filter.test(employee)) {
emp.add(employee);
}
}
return emp;
}
4.2.4 根据具体的策略实现业务
public void test2() {
// 按照年龄过滤
List<Employee> employees = filterEmployee(this.employees, new MyFilterByAge());
// 按照工资过滤
List<Employee> employees1 = filterEmployee(this.employees, new MyFilterBySalary());
}
4.3 使用匿名内部类进行优化
解决策略设计模式,针对每种策略都要定义一个实现接口的策略类的问题
package com.lz.java8.lambda;
public interface MyFilter {
public boolean test(Employee employee);
}
public List<Employee> filterEmployee(List<Employee> employees, MyFilter filter) {
List<Employee> emp = new ArrayList<>();
for (Employee employee : employees) {
if (filter.test(employee)) {
emp.add(employee);
}
}
return emp;
}
// 优化方式二: 匿名内部类
public void test3() {
// 按照年龄过滤
List<Employee> employees = filterEmployee(this.employees, new MyFilter() {
@Override
public boolean test(Employee employee) {
return employee.getAge() > 35;
}
});
// 按照工资过滤
List<Employee> employees1 = filterEmployee(this.employees, new MyFilter() {
@Override
public boolean test(Employee employee) {
return employee.getSalary() > 3000;
}
});
}
4.4 使用lambda表达式
package com.lz.java8.lambda;
public interface MyFilter {
public boolean test(Employee employee);
}
public List<Employee> filterEmployee(List<Employee> employees, MyFilter filter) {
List<Employee> emp = new ArrayList<>();
for (Employee employee : employees) {
if (filter.test(employee)) {
emp.add(employee);
}
}
return emp;
}
// 优化方式三: lambda 表达式
@Test
public void test4() {
// 按照年龄过滤
filterEmployee(this.employees, (e) -> e.getAge() > 35);
// 按照工资过滤
filterEmployee(this.employees, (e) -> e.getSalary() > 3000);
}
4.5 java8 内置的四大核心函数式接口
不用自己再去定义函数式接口,
即不用自己定义 MyFilter
4.5.1 Consumer : void accept(T t)
4.5.2 Supplier : T get()
4.5.3 Function< T, R > : R apply(T t)
4.5.4 Predicate : boolean test(T t)
4.6 优化方式四: 内置函数式接口
// 优化方式四: 内置函数式接口
public List<Employee> filterEmployee2(List<Employee> employees, Predicate<Employee> predicate) {
ArrayList<Employee> emp = new ArrayList<>();
for (Employee employee : employees) {
if (predicate.test(employee)) {
emp.add(employee);
}
}
return emp;
}
public void test5() {
// 按照年龄过滤
filterEmployee2(this.employees, (e) -> e.getAge() > 35);
// 按照工资过滤
filterEmployee2(this.employees, (e) -> e.getSalary() > 3000);
}