import java.io.Serializable
import scala.annotation.elidable
import scala.beans.BeanProperty
import scala.collection.immutable.HashMap
import scala.collection.mutable
import scala.reflect.ClassTag
/**
* keyPoint
* xx 08/07/25 09:00
*/
class ControlProgrammer extends FreeSpec {
"函数和方法" - {
"定义" - {
"方法的定义" in {
/*
* 方法是组成类的一部分. 方法有名字、类型签名, 有时方法上还有注解, 以及方法的功能。
* 在类中定义的函数就是方法. 方法是类的一部分
*/
class Demo {
// 类中定义一个方法,方法名为method,无参数,无返回值,方法的功能是控制台进行打印
def method() = println("打印")
}
}
"函数的定义" in {
/*
* Scala中的函数是一个完整的对象, 一个对象可以赋值给一个变量. Scala中用22个特质(trait)抽象出了函数的概念。这22特质从Function1到Function22
* Scala中的函数继承了这些Trait的类的对象
* 函数的定义可以单独定义,可以不依赖于类、接口或者object,而且独立存在,独立使用,并且可以赋值给变量。
*/
val adder = (x: Int, y: Int) => x + y
val adder2 = new Function2[Int, Int, Int]() {
def apply(x: Int, y: Int): Int = {
x + y
}
}
// 以上adder和adder2其实是等价的
}
}
"参数" - {
"高阶函数(函数作为参数)与头等函数: " in {
// 函数里面调用函数, 把函数作为一个函数的参数调用或返回值为函数
def apply(f: Int => String, v: Int) = f(v)
def layout[A](x: A) = "[" + x.toString + "]"
println(apply(layout, 10))
// 以上是一个高阶函数,同时它也是头等函数。头等函数的概念是:
// Scala的函数就是头等函数。你不仅可以定义和调用函数,还可以把它们写成匿名的字面量,并把它们作为值传递。
}
"可变参数: 通过在参数的类型之后放一个星号来设置可变参数(可重复的参数)" in {
// 可变参数. 类似于Java中的可变参数, 如String... , 本质上是一个数组
def demo(x: Int, y: String*) = {
println(x)
for (s <- y)
println(s)
}
demo(1, "1", "2", "3", "4")
}
"默认参数值(当你不传递参数时,给定默认参数)" in {
// Scala中可以为函数指定默认参数值
def demo(x: Int = 5, y: Int = 10) = x + y
// 调用有参数的demo(1, 2), 此时默认参数无效
val i = demo(1, 2)
println(i) // 3
// 调用无参demo(), 默认参数值生效
val ii = demo()
println(ii) // 15
}
"指定函数参数名:我们也可以通过指定函数参数名,并且不需要按照顺序向函数传递参数" in {
def demo(x: Int, y: Int) = x + y
// 显而易见, 参数名指定后, 参数顺序可以无序
val i = demo(y = 1, x = 2)
println(i)
}
}
"使用" - {
"函数的类别和使用方式" - {
"本地函数:所在函数的外部不可以访问,定义在函数内的函数又称之为局部函数,函数里面嵌套函数,这个过程称为函数嵌套" in {
// 本地函数所在函数的外部不可以访问本地函数, 本地函数可以接收外部函数传递过来的参数
def one(x: Int, y: Int) = {
// two为本地函数, 在one函数外部无法直接调用two, two可以使用one传递过来的参数, 此函数也被称为局部函数
def two(x: Int, y: Int) = x << y
two(x, y)
}
val unit = one(2, 3)
println(unit)
}
"部分应用函数:没有设置完整的参数的函数,它也是一个偏应用函数" in {
def log(s: String, i: Int) = {
println("s: " + s + ", i: " + i)
}
log("秒", 5)
log("秒", 6)
log("秒", 7)
// 以上如果我调用三次log, 那么log函数被调用三次,因为s参数一直不变,此时可以用偏应用函数做修改
val res = log("秒", _: Int) // 指定改变的Int为 _ 未定, 此时这也是一个部分应用函数
// 使用偏应用函数后log被执行一次, 然后调用res, 得到相同的结果
res(5)
res(6)
res(7)
}
"匿名函数: 匿名函数的写法,箭头左边是参数列表,右边是函数体。" in {
val sum = (x: Int, y: Int) => x + y
println(sum(1, 2))
}
"函数柯里化: 柯里化(Currying)指的是将原来接受两个参数的函数变成新的接受一个参数的函数的过程" in {
//非柯里化函数定义
def demo(x: Int, y: Int) = x + y
//柯里化使用多个参数列表
def demo1(x: Int)(y: Int) = x + y
println(demo(2, 8))
println(demo1(2)(8)) // 这里调用demo1(2)(8)实际上是依次调用两个普通函数,第一次调用使用一个参数x,返回一个函数类型的值,第二次使用参数y调用这个函数类型的值
val a = demo1(2) _ // _是指定第二个参数的占位符
val b = a(8)
println("b: " + b) // 以上两个过程就是 val b = demo1(2)(8)
}
"函数传名调用(call-by-name): 该方法在变量名和变量类型使用 => 符号来设置传名调用" in {
// 获取当前时间纳秒方法, 返回long
def time() = System.nanoTime()
// 指定高阶函数, 参数为long, 可以传入返回值为long的函数为参数
def demo(x: => Long) = println("demo方法的参数是:" + x)
println(demo(time()))
}
"Scala 递归函数: 递归函数意味着函数可以调用它本身" in {
// 满足条件就返回int值, 否则递归
def demo(x: Int, y: Int): Int = {
if (x > y) x else demo(x, y - 1)
}
demo(2, 3)
}
"尾递归" in {
// 因为递归过程中调用的方法都堆积在堆栈, 直到最后结束, 所以出现了尾递归, 每次递归的值都会返回, 也就意味着每次资源的释放
def factorial(n: Int): Int = { // 递归求阶乘
if (n <= 1) 1 else n * factorial(n - 1)
}
// 尾递归
def factorial1(acc: Int, n: Int): Int =
if (n <= 1) acc else factorial1(acc * n, n - 1)
val int = factorial1(1, 5)
println(int)
}
"当函数只有一个参数的时候,函数引用可以使用{}来代替()" in {
def demo(x: Int) = x << 2
// a和b是等值操作
val a = demo(1)
val b = demo {
1
}
val bool = a.compareTo(b)
println(bool)
}
}
}
"区别" - {
/*
* 方法不能作为单独的表达式而存在(参数为空的方法除外),而函数可以
* 函数可以作为一个参数传递到方法中, 而方法不行
* Scala中无法直接操作方法, 如果要操作方法, 必须先将其转换成函数.
* 函数必须有参数列表, 方法可以没有
* 方法名是方法调用,而函数名只是代表函数对象本身
*/
// 方法不能作为单独的表达式而存在(参数为空的方法除外),而函数可以
def m(x: Int) = x << 2
val h = (x: Int) => x << 2
// m
h
// 函数必须有参数列表, 方法可以没有
// val a = => 2 函数
def b() = println("方法")
// 方法用方法名调用, 函数名代表函数对象本身
def c() = 5
val d = () => 5
c()
d
// 函数可以作为一个参数传递到方法中, 而方法不行
def apply(f: Int => String, v: Int) = f(v)
def layout[A](x: A) = "[" + x.toString + "]"
apply(layout, 10)
}
}
}