一等函数
在 Scala 中,一等函数(first-class function)这个概念表示函数是“一等公民”,也就是说,函数可以赋值给变量,或者作为参数传递给其他函数,也可以作为函数的返回值返回给其他函数。
将函数赋值给变量
1val increase = (x: Int) => x + 1
2
3println(increase(0)) // 1
在上面的示例中,我们定义了一个函数并将其赋值给变量 increase
。然后,我们调用这个函数并将结果打印出来。
将函数作为参数传递给其他函数
1val increase = (x: Int) => x + 1
2
3def applyFunction(f: Int => Int, value: Int): Int = f(value)
4
5val result = applyFunction(increase, 0)
6
7println(result) // 1
在上面的示例中,我们定义了一个函数 applyFunction
,它接收一个函数 f
和一个整数 value
。然后,我们调用 applyFunction
函数并将 increase
函数作为参数传递给它,并将结果打印出来。
将函数作为返回值返回给其他函数
1def getIncreaseFunction(n: Int): Int => Int = (x: Int) => x + n
2
3val increase = getIncreaseFunction(1)
4
5println(increase(0)) // 1
在上面的示例中,我们定义了一个函数 getIncreaseFunction
,它接收一个整数 n
并返回一个函数。然后,我们调用 getIncreaseFunction
函数并将返回的函数赋值给变量 increase
。最后,我们调用 increase
函数并将结果打印出来。
将函数存储在数据结构中
由于函数是一等公民,因此我们可以将函数存储在 List 或 Map 这样的数据结构中,例如:
1val functions = List((x: Int) => x + 1, (x: Int) => x * 2)
2
3println(functions.map(f => f(2)))
4// Expected output:
5// List(3, 4)
在上面的示例中,我们在 List 中存储了两个函数,然后使用 map 方法将每个函数应用于 2,从而得到一个新的列表,其结果为 3 和 4。
函数字面量
Scala 中的函数字面量(function literal)是一种简洁的语法,用于定义匿名函数。
1val nums = List(1, 2, 3, 4, 5)
2
3val evenNums = nums.filter(x => x % 2 == 0)
4
5println(evenNums)
6// Expected output:
7// List(2, 4)
上面的示例中,我们在 filter 方法中传入了一个函数字面量用于过滤出偶数。
占位符语法
在函数字面量中可以使用占位符语法是函数字面量变得更加简洁,例如,如果想过滤处数组中的偶数,可以这样写:
1val nums = List(1, 2, 3, 4, 5)
2
3val evenNums = nums.filter(_ % 2 == 0)
4
5println(evenNums)
6// Expected output:
7// List(2, 4)
当编译器没有足够上下文推断参数类型时,你需要显示地给出类型信息,例如:
1def add = (_: Int) + (_: Int)
2
3println(add(1, 2)) // 3
在上面的示例中,(_: Int) + (_: Int)
定义了一个具有两个 Int 类型参数的函数。
注:
- 在函数字面量中,只有当每个参数只被调用了一次时,才能够使用占位符语法。
- 在函数字面量中如果有多个占位符,则它们分别代表了一个单独的参数,即第一个占位符代表了第一个参数,第二个占位符代表了第二个参数,以此类推。
部分应用函数
在 Scala 中,部分应用函数(Partially Applied Function)允许你固定一个函数的部分参数,并生成一个新的函数,这个新函数只接受剩余的参数。这一特性可以使得函数调用更加灵活,也易于重用。
假设你已经定义了如下日志记录函数,该函数接收日期 date
,日志级别 level
和日志消息 message
三个参数并打印一条格式化的日志信息:
1def log(date: String, level: String, message: String): Unit = {
2 println(s"[$date] - [$level] - $message")
3}
假如你想基于 log
函数创建一个日期固定为 2024-01-01
的日志记录函数,你可以使用下划线(_
)来实现:
1val todayLog = log("2024-01-01", _, _)
2
3todayLog("INFO", "This is a log message.")
4todayLog("WARN", "This is a warning message.")
5todayLog("ERROR", "This is an error message.")
6// Expected output:
7// [2024-01-01] - [INFO] - This is a log message.
8// [2024-01-01] - [WARN] - This is a warning message.
9// [2024-01-01] - [ERROR] - This is an error message.
闭包
在 Scala 中,闭包(Closure)是一种函数,它可以捕获并使用其词法范围之外的变量。简言之,如果一个函数引用了它的外部变量,那么这个函数+被引用的变量就构成了一个闭包。
函数闭包具有如下功能:
- 捕获自由变量:闭包可以使用在函数体外定义的变量。
- 影响外部状态:闭包可以捕获自由变量,因此也可以修改自由变量,从而影响外部状态。
- 动态创建函数:通过闭包可以创建更能更为丰富的函数,这些函数可以根据上下文信息操作变量。
一个简单的闭包示例如下:
1var greeting = "Hello"
2val sayHelloTo = (name: String) => s"$greeting, $name!"
3
4println(sayHelloTo("World")) // Hello, World!
5
6greeting = "Hola"
7println(sayHelloTo("World")) // Hola, World!
在上面的示例中,我们定义了一个变量 greeting
,并创建了一个闭包 sayHelloTo
,该闭包引用了外部变量 greeting
。当我们修改变量 greeting
的值时,闭包 sayHelloTo
中也会使用修改后的值。