【Scala】03 函数
1、Scala的方法语法:
object Hello { def main(args : Array[String]) : Unit = { // scala 允许在方法的声明中再声明方法,并且调用 def someFunction(x : Int, y : String): Unit = { println("this function has been called! (In this main method)") } // 调用方法中声明的 someFunction(100, "str") // 调用这个对象声明的 val returnVal = Hello.someFunction(2, "ol") } // 在对象中也可以声明方法,支持和方法中重名 def someFunction(x : Int, y : String): Int = { println("this function has been called! (In this single instance)") return 100 } }
2、参数和返回值
// 没有参数 没有返回值 def method01(): Unit = { print("method01") } // 有参数 没有返回值 def method02(x : Int, y : String): Unit = { print("method02") } // 有参数 有返回值 def method03(x : Int, y : String): Int = { print("method01") return 100 } // 没有参数 有返回值 def method04(): Int = { return 300 }
3、参数特性
// 在参数类型后面加上*号表示该参数是可变参数 def method(str : String*): Unit = { println(str) } method("hello", "scala") // 打印结果是一个数组序列 ArraySeq(hello, scala) // 多个参数必须将可变参数放在最后一个 def method2(x : Int, y : Double, string: String*): Unit = { } // 默认参数 def method3(name : String = "这是默认值"): Unit = { println(name) } method3() // 这样无参也可以被调用,因为已经设定了一个默认值在这里 C++是支持这种操作的 method3("jojo") // 带名参数,这个参数的意思是,你可以在调用的时候强制指定注入的参数是给哪个参数的 def method4(name : String, age : Int, gender : Boolean): Unit = { } // 带名参数的意义就在于支持打乱顺序入参 method4(gender = true, name = "张三", age = 30)
4、函数特性:
// Lambda表达式特性 // 1、可以省略return,Scala支持 函数体的最后一段代码最为返回的结果 def f0(str : String) : String = { str // 这里就省略的了 return } println(f0("param")) // 2、函数体只有一段代码可以省略阔话表示 def f1(str : String) : String = str // 3、如果类型可以推断出来,则不需要强制声明返回类型是什么 def f2(str : String) = str // 4、写了Return就必须声明返回类型 def f3(str : String) : String = return str // 5、明确了返回类型为Unit,则 return没有任何意义 def f4(str : String) : Unit = return str // 这个str无意义 // 6、Unit返回类型 可以直接向Java一样声明 def f5(str : String) { println(str) } // 7、没有参数,但是声明的时候写了,调用可以没有括号,加括号也行 def f6() { println("str") } f6() // 这样调用正常 f6 // 这样调用也没问题 // 8、如果声明的时候就没有参数列表,调用绝不能写括号 def f7 { println("str") } f7 // 调用正常 // f7() // 这样调用编译报错 // 如果不关心名称,只需要逻辑处理,这里也可以匿名声明 (name : String) => { println(name) }
5、函数可以作为参数注入
val funcRef = (string : String) => { println(string) } // 定义一个函数,参数可以是一个函数 def funcForFunc(function: String => Unit): Unit = { function("asdas"); } // 这里很奇怪的一点是,参数为什么可以在里面注入,而外面只需要声明入参数函数? funcForFunc(funcRef) // 我有点想明白了,这样的话,参数是顶死的,但是允许套用不同的方法,只要方法符合这个参数的方法类型要求即可被套用 // 语法缩写 // 1、参数类型可以省略不写,只要能够被推导出来 funcForFunc((name) => {println(name)}) // 2、参数只有一个可以不写参数括号 funcForFunc(name => {println(name)}) // 3、执行的语句只有一行可以不写花括号表示 funcForFunc(name => println(name)) // 4、参数只使用过一次的情况,可以不声明参数了,直接使用下划线表示 funcForFunc(println(_)) // 5、最后是调用推断 funcForFunc(println)
用法案例介绍:
// 定义一个二元运算的函数,只操作固定的1和2个值,具体的运算由注入的函数实现 def calcForThis(fun : (Int, Int) => Int) : Int = { fun(100, 200) } val sum = (a : Int, b : Int) => a + b val minus = (a : Int, b : Int) => a - b val max = (a : Int, b : Int) => if (a > b) a else b val min = (a : Int, b : Int) => if (a > b) b else a // 调用 val s = calcForThis(sum) // val s = calcForThis(minus) // val s = calcForThis(max) // val s = calcForThis(min) // 匿名函数简化 // 原始完整调用 println(calcForThis((a : Int, b : Int) => a + b)) // 对匿名简化 println(calcForThis((a, b) => a + b)) // 参数简化 println(calcForThis( _ + _))
这和Java或者之前学习的编程不太一样
我们创建各种方法,是为了对参数进行处理,方法是为了封装逻辑
现在感觉是反过来,参数是既定的,封装的成方法了,这个感觉写起来像那个接口的感觉一样
6、函数的进一步操作
// 1、在函数体中声明函数,然后又调用函数 def foo() { println("foo ...") } foo() // 2、作为值进行传递 def foo2() : Int = { 100 } val res = foo2() // 将函数的返回值传递给 res val res2 = foo2() _ // 将函数自己作为值传递给 res2 val res3:() => Int = foo2 // 如果明确变量类型? 可以不使用下划线将函数作为整体传递给参数 // 3、函数入参 作为函数参数 def subjectFunction(paramFunction : (Int, Int) => Int): Int = { paramFunction(100, 314) // subjectFunction 主体函数, paramFunction参数函数 } subjectFunction(_ + _) subjectFunction(_ * _) // 4、函数作为返回值返回 def f1() = { // 声明外层一个f1函数 def f2() = { // 在内层中声明一个f2函数 } f2 _ // 并且返回f2这个函数 } val s = f1() // 调用就是把f2函数传递给s
7、对集合的一些处理操作
// 对数组进行操作,如何操作作为一个抽象来定义,处理完毕返回一个新的数组 def arrOp(arr : Array[Int], op : Int => Int) : Array[Int] = { for (elem <- arr) yield op(elem) } // 声明一个加一操作 def increaseOne(elem : Int) = { elem + 1 } // 使用 val sourceArr = Array(1, 10, 30, 100) // 声明一个原始的数组 val newArr = arrOp(sourceArr, increaseOne) // 注意这里给方法的时候是把方法本身传递,而不是调用 println(newArr.mkString(",")) // 简化调用 val newArr2 = arrOp(sourceArr, _ + 1)
8、案例练习:
// 声明匿名函数给fun变量 val fun = (a : Int, a2 : String, a3 : Char) => { if(a == 0 && a2 == "" && a3 == '0') false else true } // 调用 println(fun(0, "", '0'))
9、初见柯里化
// 但是这段声明的语法看不太懂 def func(i: Int): String => (Char => Boolean) = { def f1(s : String) : Char => Boolean = { def f2(c : Char) : Boolean = { if(i == 0 && s == "" && c == '0') false else true } f2 } f1 } // 简写 def func2(i: Int): String => (Char => Boolean) = { (s : String) => { (c : Char) => { if(i == 0 && s == "" && c == '0') false else true } } } // 最外层有声明参数类型,也可以被省略 def func3(i: Int): String => (Char => Boolean) = { s => c => if(i == 0 && s == "" && c == '0') false else true } // 柯里化? def func4(i: Int)(s: String)(c : Char):Boolean = { if(i == 0 && s == "" && c == '0') false else true } // 这里肯定看不懂 println(func(0)("")('0')) // 这样来拆解出来 是这样调用的,只是没有变量接受,直接匿名进行调用了 val resultFunction1 = func(0) val resultFunction2 = resultFunction1("") val res = resultFunction2('0') println(res)
10、案例:
// 闭包 & 柯里化 // 闭包概念 : 一个函数,访问了外部的局部变量的值,这个函数和所在的范围称为闭包 // 意义在于外部函数知晓完毕出栈之后,需要保留部分的参数内容,给内部函数进行计算和调用 // 案例 将固定加数作为另一个参数传入,但是是作为第一层参数传入 def addByParam():Int => Int = { val a = 4 def addByPa2(b : Int): Int = { a + b } addByPa2 } def addByParam2(a:Int):Int => Int = { def addByPa2(b : Int): Int = { a + b } addByPa2 } def addByParam3(a:Int):Int => Int = a + _ // 柯里化 把一个参数列表的多个参数,变成多个参数列表 val res = addByParam2(39)(42) def addCurrying(a : Int)(b : Int) = { a + b } addCurrying(39)(42)
11、递归 Recursive
/** * Scala的递归 * 1、方法调用的是自身 * 2、必须存在可以结束的逻辑 * 3、参数应该有规律 * 4、递归必须声明返回类型 */ // 阶乘案例 def factorial(n : Int): Int = { if (n == 0) return 1 factorial(n - 1) * n } // 尾递归 def tailFact(n : Int)= { def loop(n : Int, currRes : Int) : Int = { if (n == 0) return currRes loop(n - 1, currRes * n) } loop(n, 1) }
12、抽象控制
// 控制抽象 /** * 这里演示一段Java写法 */ val a = 100 // 声明一个常量 def someFunction(s : Int) = { // 声明函数 println(s) } someFunction(100) // 一般来说就是直接注入实际可见的值 someFunction(a) // 或者是变量 /** * Scala 希望不出现这些变量和字面值 * 因为函数代表了一切内容 */ def returnTen() = 10 someFunction(returnTen()) // 要以这种方法来进行入参的表达方式,就是控制抽象
13、传名参数
// 传名参数,强调的是参数是一段代码块,执行包括代码块中的内容 def fun(a : => Int) = { println(s"a : ${a}") println(s"a : ${a}") } def f1() = { println("f1 called") 12 } // 使用 fun(f1()) // f1被调用了两遍 a即表示函数,出现一次调用一次 fun(23) // 为什么可以传递23? /** * 因为23就是一个函数的返回 */ () => 23
14、实现自定义循环的案例
// 实现自定义循环 def customWhile(condition : Boolean):(=> Unit)=> Unit= { def doCustomLoop (operate : => Unit): Unit = { if (condition) { operate customWhile(condition) (operate) } } doCustomLoop _ } // 柯里化表达 def customWhile2(condition: => Boolean)(operate: => Unit): Unit = { if (condition) { operate customWhile2(condition)(operate) } } var n = 10 customWhile2(n > 0) { println(n) n -= 1 }
15、懒加载处理
val sum = (a : Int,b : Int) => { println("sum has been called") a + b } val minus = (a : Int,b : Int) => { println("minus has been called") a - b } val result1 : Int = minus(11, 31) // 惰性加载 lazy val result2 : Int = sum(11, 31) println("- - - 函数应该被调用了 - - -") println(s"函数应该被调用了 ${result2} ${result1}")