package day04
import java.io.{File, PrintWriter}
class Demo(desc: String) {
override def toString: String = desc
}
object Demo {
def main(args: Array[String]): Unit = {
val d = new Demo("Scala 函数")
println(d)
test_1()
test_2()
test_3()
test_4()
test_5()
// test_6()
test_7()
test_8()
test_9()
test_10()
}
// 类成员函数,和 Java 的成员方法一致,略
/**
* 局部函数:在函数内部定义函数
* 在局部函数中可以访问外部定义的变量
*
*/
def test_1(): Unit = {
val v_1 = "hello "
def innerFun(): Unit = {
println(v_1 + "inner function")
}
innerFun()
}
/**
* 函数字面量(就是匿名函数)
* 函数是 Scala 中的“头等公民”(W3C中这句话不知道什么意思。。。)
* 匿名函数看起来不能指定返回值类型(语句最后一行执行的结果就是返回值)
*/
def test_2(): Unit = {
// 函数字面量
var increase = (x: Int) => x + 1
println(increase(6))
// Lists 不可修改序列
val someNumbers = List(-11, -10, -5, 0, 5, 10)
// 在 foreach 中使用匿名函数
someNumbers.foreach(i => println(i))
// 在 filter 中使用匿名函数
println(someNumbers.filter(i => i > 0))
}
/**
* 部分应用的函数(其实就是 python 中的偏函数)
*/
def test_3(): Unit = {
// sum2 是 sum1 的简化写法
// Scala 允许使用“占位符”下划线”_”来替代一个或多个参数,只要这个参数值函数定义中只出现一次,Scala编译器可以推断出参数。
def sum1 = (a: Int, b: Int, c: Int) => a + b + c
def sum2 = (_: Int) + (_: Int) + (_: Int)
// 定义偏函数
val b = sum2(1, _: Int, 3)
println(b(10))
}
// 闭包
def test_4(): Unit = {
var more = 1
// 定义:引用到函数外面定义的变量,定义这个函数的过程是将这个自由变量捕获而构成一个封闭的函数。
val addMore1 = (i: Int) => i + more
println(addMore1(10))
// 当这个自由变量发生变化时,Scala 的闭包能够捕获到这个变化,因此 Scala 的闭包捕获的是变量本身而不是当时变量的值。
more = 100
println(addMore1(10))
// 如果变量在闭包在发生变化,也会反映到函数外面定义的闭包的值。
val addMore2 = (i: Int) => {
more = more + i
more
}
println(addMore2(1000))
println(addMore2(1000))
println(more)
}
// 函数–可变参数,命名参数,缺省参数
def test_5(): Unit = {
// 可变参数
def echo(args: String*): Unit = {
args.foreach(s => println(s))
}
echo("1", "2", "3")
val arr = Array("What's", "up", "doc?")
echo(arr: _*) // 奇奇怪怪的语法
// 命名参数:使用命名参数允许你使用任意顺序传入参数
def speed(distance: Float, time: Float): Float = distance * time
println(speed(10, 20))
println(speed(time = 10, distance = 20))
// 缺省参数值:Scala 在定义函数时,允许指定参数的缺省值,从而允许在调用函数时不指明该参数,此时该参数使用缺省值。缺省参数通常配合命名参数使用
def printTime(out: java.io.PrintStream = Console.out, divisor: Int = 1) = out.println("time = " + System.currentTimeMillis() / divisor)
printTime()
printTime(divisor = 666)
}
/**
* 尾递归:在最后一行调用自身的递归
* 理论上来说,使用递归的性能不如使用循环
* 但是 scala 的编译器会针对尾递归进行优化(优化为循环)
*/
def test_6(): Unit = {
def test(i: Int): Unit = {
if (i > 100) {
println("OVER")
throw new Exception("boom!") // 用来显示调用栈
} else {
test(i + 1) // 只要最后一行不是调用自身,就不构成尾递归
}
// i + 1
}
test(0)
}
// 减低代码重复
def test_7(): Unit = {
// 函数可以作为参数传递(可以用来简化一些逻辑基本相似的代码)
def check(s1: String, s2: String,
checker: (String, String) => Boolean): Boolean = { // 该函数的类型为 (String,String ) =>Boolean,可以匹配任意使用两个 String 类型参数,返回值类型为 Boolean 的函数。
checker(s1, s2)
}
def test(s1: String, s2: String): Boolean = {
false
}
println(check("aaa", "aaa", _.equals(_)))
println(check("aaa", "aaa", test(_, _)))
}
// 柯里化函数(没什么卵用):柯里化是把接受多个参数的函数变换成接受一个单一参数的函数
def test_8(): Unit = {
def test(i1: Int)(i2: Int)(i3: Int): Int = {
i1 + i2 + i3
}
println(test(1)(2)(3))
}
// 创建新的控制结构:根据 scala 的高级函数特性,可以实现一些语法比较固定的控制结构
def test_9(): Unit = {
// 自定义控制结构
def withPrintWriter(file: File, op: PrintWriter => Unit) {
val writer = new PrintWriter(file)
try {
op(writer)
} finally {
writer.close()
}
}
// 使用
withPrintWriter(
new File("date.txt"),
writer => writer.println(new java.util.Date)
)
}
// 传名参数
def test_10(): Unit = {
val assertionsEnabled = true
// 传值参数
def myAssert(predicate: () => Boolean) =
if (assertionsEnabled && !predicate())
throw new AssertionError
// 传名参数
def myNameAssert(predicate: => Boolean) =
if (assertionsEnabled && !predicate)
throw new AssertionError
// 区别:传值参数是先计算后传值,传名参数相反
myAssert(() => {
// do something
false
})
myNameAssert({
// do something
false
})
}
}