Scala 函数
最后修改于 2023 年 1 月 10 日
在本文中,我们介绍 Scala 中的函数。
一个函数
一个函数是将零个或多个输入值映射到零个或多个输出值。
使用函数,我们可以减少代码重复并提高其清晰度。更复杂的任务可以使用函数分解为更简单的单元。
函数可以赋值给变量,作为参数传递给函数,或从其他函数返回。
在 Scala 中,有两种类型的函数:val 函数和 def 函数。这两种类型的函数之间存在一些技术差异。在需要时,def 函数会转换为带有下划线运算符的 val 函数。这种转换通常是自动进行的,因此区别有些模糊。
Scala 主函数
主函数是 Scala 应用程序的入口点。它使用 `@main` 注释进行装饰。
@main def main() =
println("main function is an entry point")
主函数的名称和参数由 `=` 运算符终止。运算符后面是代码块。在 Scala 3 中,空格是语法的一部分。它用于分隔函数体。
println("main function is an entry point")
`println` 是一个内置函数,它将文本打印到终端并添加换行符。
$ scala main.scala main function is an entry point
主函数可以接受参数。
@main def main(args: String*) =
args.foreach(println)
在程序中,主函数可以接受任意数量的字符串参数。
$ scala main.scala an old falcon an old falcon
返回值
传统上,`return` 关键字用于从函数返回一个值。在 Scala 中,`return` 关键字是可选的。
def square(x: Int): Int =
// return x * x
x * x
@main def main() =
println(square(5))
我们定义了一个 `square` 函数。
def square(x: Int): Int =
`def` 关键字后面是函数名。在方括号中,我们提供函数参数。括号后面是冒号和返回值的类型。在 `=` 字符之后,我们提供函数体。
// return x * x x * x
在 Scala 中,`return` 关键字是可选的。最后一个表达式的值会自动返回给调用者。
println(square(5))
我们使用数字 5 作为参数调用 `square` 函数。计算出的值将打印到控制台。
$ scala main.scala 25
可变数量的参数
使用星号定义可变数量的参数。
def mysum(vals: Int*): Int =
var total = 0
for n <- vals do
total += n
total
@main def main() =
val s1 = mysum(1, 2, 3)
val s2 = mysum(1, 2, 3, 4)
val s3 = mysum(1, 2, 3, 4, 5)
println(s1)
println(s2)
println(s3)
该程序包含一个 sum 函数的定义。该函数可以接受任意数量的参数。这些参数在函数体内作为值序列可用。
def mysum(vals: Int*): Int =
var total = 0
for n <- vals do
total += n
total
这是自定义 `mysum` 函数的定义。`vals` 是一个整数值序列。通过 for 循环,我们计算最终的总和。
val s1 = mysum(1, 2, 3) val s2 = mysum(1, 2, 3, 4) val s3 = mysum(1, 2, 3, 4, 5)
我们将三个、四个和五个值传递给函数。
$ scala main.scala 6 10 15
Scala 默认函数参数
Scala 函数参数可以具有默认值;如果未为参数提供值,则使用这些默认值。
def power(x: Int, n: Int = 2): Int =
if n == 2 then
x * x
var res = 1
var i = 0
while i < n do
res *= x
i += 1
res
@main def main() =
val r1 = power(3)
println(r1)
val r2 = power(3, 3)
println(r2)
我们有一个 `power` 函数。该函数有一个带有隐式值的参数。我们可以用一个或两个参数调用该函数。
def power(x: Int, n: Int = 2): Int =
`power` 函数的第二个参数是隐式的。如果未提供,其值为 2。
$ scala main.scala 9 27
Scala 匿名函数
匿名函数没有名称。在许多情况下,定义一个命名函数是多余的。
@main def main() =
val nums = List(1, 2, -3, -4, 5)
val pos = nums.filter(e => e > 0).map(e => e * 2)
println(pos)
val neg = nums.filter(_ < 0)
println(neg)
nums.foreach(e => print(s"$e "))
println
我们有一个整数列表。我们在列表上调用了一些 `filter`、`map` 和 `foreach` 函数。
val pos = nums.filter(e => e > 0).map(e => e * 2)
`filter` 和 `map` 是作用于列表元素的函数。它们将一个谓词函数(返回布尔值的函数)作为参数。在我们的例子中,谓词是匿名函数。
val neg = nums.filter(_ < 0)
这是简化的语法。
$ scala main.scala List(2, 4, 10) List(-3, -4) 1 2 -3 -4 5
Scala val 函数
使用 `val` 关键字,我们定义函数类型。
val square = (x: Int) => x * x
val triple: (x: Int) => Int = (x) => x * x * x
@main def main() =
val nums = List(1, 2, 3, 4, 5)
val res = square(3)
println(res)
val res2 = triple(5)
println(res2)
val res3 = nums.map(square)
println(res3)
val square2 = square
println(square2(5))
该程序定义了两个 val 函数。
val square = (x: Int) => x * x
函数类型绑定到 `square` 标识符。函数的参数与函数体通过胖箭头 `=>` 运算符分隔。`square` 标识符的类型声明被省略,因为 Scala 能够推断它。
val triple: (x: Int) => Int = (x) => x * x * x
这里我们省略了函数参数的类型,但提供了 `tripe` 标识符的类型。(不能同时省略两个声明。)
val res = square(3) println(res val res2 = triple(5) println(res2) val res3 = nums.map(square) println(res3)
val 函数的调用方式与 def 函数完全相同。
$ scala main.scala 9 125 List(1, 4, 9, 16, 25) 25
val 函数和 def 函数的一个区别是 def 函数总是被求值。
val getNano = System.nanoTime
def getNano2 = System.nanoTime
@main def main() =
println(getNano)
Thread.sleep(300)
println(getNano)
println("-----------------")
println(getNano2)
Thread.sleep(300)
println(getNano2)
`getNano` 和 `getNano2` 函数返回当前的纳秒时间。
val getNano = System.nanoTime
`getNano` 函数只求值一次,后续调用返回相同的值。
def getNano2 = System.nanoTime
`getNano2` 函数始终被求值。
$ scala main.scala 32724329770177 32724329770177 ----------------- 32724630310958 32724930546360
Scala 嵌套函数
嵌套函数,也称为内部函数,是在另一个函数内部定义的函数。
def minmax(x: Int, y: Int) =
val min = (x: Int, y: Int) => if x < y then x else y
val max = (x: Int, y: Int) => if x > y then x else y
var mn = min(x, y)
var mx = max(x, y)
(mn, mx)
@main def main() =
val res = minmax(100, 13)
println(s"min: ${res._1} max: ${res._2}")
val res2 = minmax(0, -13)
println(s"min: ${res2._1} max: ${res2._2}")
在该程序中,我们有一个 `minmax` 函数,其中包含两个嵌套函数:`min` 和 `max`。
$ scala main.scala min: 13 max: 100 min: -13 max: 0
Scala 高阶函数
高阶函数通过将其他函数作为参数或返回其他函数来操作它们。
def process(data: List[Int], f: (e: Int) => Int): List[Int] =
data.map(f)
@main def main() =
val nums = List(1, 2, 3, 4, 5, 6)
val res1 = process(nums, e => e * e)
println(res1)
val res2 = process(nums, e => e + 1)
println(res2)
`process` 是一个高阶函数。
def process(data: List[Int], f: (e: Int) => Int): List[Int] =
data.map(f)
`process` 高阶函数将函数应用于列表。
val res1 = process(nums, e => e * e) ... val res2 = process(nums, e => e + 1)
我们将两个不同的匿名函数传递给 `process` 函数。
$ scala main.scala List(1, 4, 9, 16, 25, 36) List(2, 3, 4, 5, 6, 7)
Scala 闭包
闭包是一个匿名嵌套函数,它保留对定义在闭包体外部的变量的绑定。
闭包可以持有自己独特的状态。当我们创建函数的新实例时,状态会得到隔离。
def intSeq(): () => Int =
var i = 0
return () => { i += 1; i }
@main def main() =
val nextInt = intSeq()
println(nextInt())
println(nextInt())
println(nextInt())
println(nextInt())
println("-------------------")
val nextInt2 = intSeq()
println(nextInt2())
println(nextInt2())
我们有一个 intSeq 函数,它生成一个整数序列。它返回一个递增 `i` 变量的闭包。
def intSeq(): () => Int =
var i = 0
return () => { i += 1; i }
在函数中定义的变量具有局部函数作用域。但是,在这种情况下,即使在 `intSeq` 函数返回后,闭包仍绑定到 `i` 变量。
val nextInt = intSeq()
调用了 `intSeq` 函数。它返回一个递增计数器的函数。返回的函数通过关闭变量 `i` 来形成闭包。闭包绑定到 `nextInt` 值。
println(nextInt()) println(nextInt()) println(nextInt()) println(nextInt())
我们调用闭包四次。
val nextInt2 = intSeq() println(nextInt2()) println(nextInt2())
下一次调用 `intSeq` 函数会返回一个新的闭包。这个新的闭包有自己独立的状态。
$ scala main.scala 1 2 3 4 ------------------- 1 2
Scala 扩展函数
扩展函数为现有类型添加功能。扩展函数使用 `extension` 关键字创建。
extension (value: Int)
def isOdd = value % 2 == 0
def isEven = value % 2 != 0
def times(f: Any => Unit, v: Any): Unit =
var i = 0
while i < value do
f(v)
i += 1
@main def main() =
val n = 4
println(n.isOdd)
println(n.isEven)
n.times(println, "falcon")
在该程序中,我们将三个扩展函数添加到 `Int` 类型。
extension (value: Int)
def isOdd = value % 2 == 0
def isEven = value % 2 != 0
def times(f: Any => Unit, v: Any): Unit =
var i = 0
while i < value do
f(v)
i += 1
`isOdd` 和 `isEven` 函数检查整数是偶数还是奇数。`times` 函数执行给定的函数 n 次。
$ scala main.scala true false falcon falcon falcon falcon
Scala 成员函数
成员函数是在 Scala 类中定义的函数。
class Cat:
def talk() =
println("meow")
@main def main() =
val missy = Cat()
missy.talk()
`talk` 函数定义在 `Cat` 类中。
val missy = Cat() missy.talk()
我们创建 `Cat` 对象并通过点运算符调用成员函数。
$ scala main.scala meow
在本文中,我们学习了 Scala 函数。