Scala 函数式编程
Scala 函数式编程
Scala是一门既面向对象,又面向过程的语言。在Scala中,函数与类、对象地位是一样,所以说scala的面向过程其实就重在针对函数的编程 了,所以称之为函数式编程
1、面向对象编程:
解决问题,分解问题,行为,属性,然后通过对象的关系以及行为的调用来解决问题
对象:用户
行为:登录,连接JDBC,读取数据库
属性:用户名,密码
Scala 语言是一个完全面向对象编程语言,万物皆对象
对象的本质:对数据和行为的一个封装
2、函数式编程
解决问题时,将问题拆分成一个一个的步骤,将每个步骤进行封装(函数),通过调用这些封装好的步骤,解决问题
例如:请求->用户名、密码->连接JDBC->读取数据库
Scala语言是一个完全函数式编程语言,万物皆函数
函数的本质:函数可以当成一个值进行传递
在scala
中函数式编程和面向对象编程完美融合在一起了
一、函数基础
1、函数基础语法
2、代码示例
需求:定义一个函数,实现将传入的名称打印出来
直接使用def 关键字定义函数 sayHi 是一个函数名称,里面是参数
然后调用函数直接在main方法里函数名,里面把参数传进去就行了
package scala_xuexi01
class test15_hanshu {
}
object test15_hanshu{ //单例对象
def main(args: Array[String]): Unit = {
def sayHi(name:String):Unit ={
println("hi"+name)
}
//调用函数
sayHi("alice") //直接传入name参数
}
}
3、函数和方法的调用
函数和方法的区别,函数在main方法里面写,然后再里面调用,直接函数名,里面传入参数,方法是和main方法同级别的,所以要写在外面,还有通过对象调用函数,其实都是一样的
package scala_xuexi01
class test15_hanshu {
}
object test15_hanshu{ //单例对象
def main(args: Array[String]): Unit = {
def sayHi(name:String):Unit ={
println("hi:"+name)
}
//调用函数
sayHi("alice") //直接传入name参数
//调用对象方法
test15_hanshu.sayHi("bob")
//获取方法返回值
val result = test15_hanshu.sayHi("carry")
println(result)
}
//定义对象的方法
def sayHi(name:String):Unit={ //定义在外面的叫做方法,里面在里面的叫做函数,因为main方法也是一个方法
println("Hi:"+name) //定义的方法和他是同级的,所以要写在外面,函数写在main方法的里面
}
sayHi("aex")
}
4、函数和方法的区别
1)核心概念:
- 为完成某一功能的程序语句的集合,称为函数
- 类中的函数称之为方法
2)核心概念:
3. Scala 语言可以在任何的语法结构中声明任何的语法
4. 函数没有重载和重写的概念,方法可以进行重载和重写
5. Scala中函数可以嵌套定义
二、函数定义
1、函数定义
- 函数1:无参数,无返回值
- 函数2:无参数,有返回值
- 函数3:有参数,无返回值
- 函数4:有参数,有返回值
- 函数5:多参数,无返回值
- 函数6:多参数,有返回值
- 注意:有参数没有返回值比较适合字符串拼接,比如name + “在打游戏”,多参数有返回值,比较适合两个数字相加,然后返回a+b 然后有返回值设置哈返回值类型,如果有多种类型返回值那就设置为
Any
,返回值写在最后一行,可以用return
,也可以不用,有返回值得用println()输出函数
,才能返回值才会显示出来,
2、代码示例
package scala_xuexi01
class test16_hanshuback {
}
object test16_hanshuback{
def main(args: Array[String]): Unit = {
//1. 函数1:无参数,无返回值
def hanshu(): Unit = {
println("这是一个没有参数,没有返回值的函数")
}
hanshu()
//2. 函数2:无参数,有返回值
def f2():Int = {
println("这是一个没有参数,有返回值的函数")
return 1 //下面这个是返回值 可以用return 不用的话也可以
}
println(f2()) //函数要返回值的话,要用println()把函数输出
//3. 函数3:有参数,无返回值
def f3(name:String):Unit={
println(name+"在打游戏")
}
println(f3("黄某")) //有参数把参数输入进去就行了,用print()输出函数才有返回值
//4. 函数4:有参数,有返回值
def f4(name:String):String={
println("hi"+name)
return "有参数有返回值" // 最后这一行是返回值,返回值也可以加参数的
}
println(f4("小明"))
//5. 函数5:多参数,无返回值
def f5(name:String,age:Int,aex:String):Unit={
println(s"${name}今年${age}岁了,他是${aex}孩子")
}
println(f5("小明",15,"男"))
//6. 函数6:多参数,有返回值 应用场景两个数相加
def f6(a:Int,b:Int):Int={
println(s"${a}+${b}=${a+b}")
return a+b //返回这两个数字
}
println(f6(6,7)) //传入参数,输入函数
}
}
三、函数参数
1、基本说明
- 可变参数
- 如果参数列表中存在多个参数,那么可变参数一般放置在最后
- 参数默认值,一般将有默认值的参数放置在参数列表的后面
scala
里面还可以定义参数的默认值 - 带名参数
注意:可变参数在参数后面加上*
就成可变参数了,比如name:String*
,定义了可变参数之后,参数就会变成集合类型了,然后可以有很多个值,如果存在多个参数,可变参数一定要再定义参数的最后一个,不然会报错,系统分不清到底是什么意思。然后参数默认值,定义参数的时候=然后赋值,比如name:String="小明"
,定义了参数默认值后,调用函数的时候,可以不传入参数也不会报错,如果传入了就把这个默认值代替了,最后一个带名参数,在调用函数传入参数的时候,如果有多个传入,想改变传入参数的位置,那么就直接age =18
,直接把参数名写出来,就可以写在前面,改变传入参数的位置了,这么做主要是为了,在定义了参数默认值的时候,就可以少写一点参数了,直接传一个参数写在前面就完事了
2、代码示例
package scala_xuexi01
class test17_hasnhucanshu {
}
object test17_hasnhucanshu{
def main(args: Array[String]): Unit = {
//1. 可变参数
def f1(str:String*):Unit={ //可变参数,想要定义可变参数直接在String后面加上*
println(str) //注意这时候已经不是简单的String类型了,现在成集合类型了
}
f1("alice")
f1("aaa","bbb","ccc")
//2. 如果参数列表中存在多个参数,那么可变参数一般放置在最后
def f2(str1:String,str2:String*): Unit ={ //可变参数一定放在最后一个,放在前面他会报错
println(str1)
println(str2)
}
f2("hello","daad","ddad","hhe")
//3. 参数默认值,一般将有默认值的参数放置在参数列表的后面 `scala`里面还可以定义参数的默认值
def f3(name:String="小明"):Unit={ //直接在这个参数后面=一个默认的值,到时候穿了参数这个就相当于没有
println(name + "在打游戏") //要是参数有默认值,那么调用函数的时候不传入参数也不会报错
}
f3()
//4. 带名参数
def f4(name:String,age:Int):Unit={ //在参数有默认值的情况下,那么就只用传一个参数就行了,
println(name+age+"岁") //所以带名参数改变位置是这个作用
}
f4(age = 13,name = "小明") //要是想不按照字段定义时的顺序来,那么参数就加上名字赋值,就可以改变顺序
}
}
四、函数至简原则
函数至简原则:能省就省
1、至简原则细节
- return 可以省略,Scala 会使用函数体的最后一行代码作为返回值
- 如果函数体只有一行代码,可以省略花括号{}
- 返回值类型如果能够推断出来,那么可以省略(: 和返回值类型一起省略)
- 如果有return ,则不能省略返回值类型,必须指定
- 如果函数明确声明unit,那么即使函数体中使用return关键字也不起作用
- Scala 如果期望是无返回值类型,可以省略等号
- 如果函数无参数,但是声明了参数列表,那么调用时,小括号,可加可不加
- 如果函数没有参数列表,那么小括号可以省略,调用时小括号必须省略
总结:Scala
会使用函数体的最后一行作为返回值,return可以省略
,如果函数体只有一行代码那么花括号{}
可以省略,返回值类型如果能够推断出来,那么可以省略:
和返回值一起省略,如果有return
那么就不能省略返回值,必须要写
,如果函数明确声明了Unit
,那么即使函数体中使用return
关键字也不会起作用,如果是一个没有参数的函数,那么定义函数名称的括号都可要可不要,
如果只关心逻辑处理,不关心名称,那么连函数名称都可以不要,例如{name:String}=>{println(name)}
,这是匿名函数
,也叫做lamda
表达式,在js
里面叫做箭头函数
2、代码示例
package scala_xuexi01
//函数至简原则
class test18_hanshujianhua {
}
object test18_hanshujianhua{
def main(args: Array[String]): Unit = {
//1. return 可以省略,Scala 会使用函数体的最后一行代码作为返回值
def f0(name:String):String={
return name //函数体就是返回值,可以不需要return,因为在函数体最后一行就是返回值
}
println(f0("小明"))
//2. 如果函数体只有一行代码,可以省略花括号{}
def f2(name:String):String=name //这个那么就是返回值,因为不要花括号嘛{},如果只有一行代码可以不要
println(f2("这是一个省略花括号的函数"))
//3. 返回值类型如果能够推断出来,那么可以省略(: 和返回值类型一起省略)
def f3(name:String) = name //当之前的参数定义了类型之后,返回值类型的都可以省略
println(f3("这是个连返回值的类型的都省了的函数"))
//4. 如果有return ,则不能省略返回值类型,必须指定
def f4(name:String):String={ //当函数体使用了return,那么就必须有返回值,就不能省了
return name
}
println(f4("当函数体使用了return,那么就必须有返回值,就不能省了"))
//5. 如果函数明确声明unit,那么即使函数体中使用return关键字也不起作用
def f5(name:String):Unit={
return name
}
println(f5("当返回值声明了Unit类型之后,那么使用return也不起作用"))
//6. Scala 如果期望是无返回值类型,可以省略等号
def f6(name:String){
println(name)
}
println(f6("当没有返回值的时候,可以省略返回值的类型,等号也可以省略"))
//7. 如果函数无参数,但是声明了参数列表,那么调用时,小括号,可加可不加
def f7(){
println("这是一个没有参数的函数,调用的时候可加可不加括号,返回值类型也可以不用加")
}
println(f7)
//8. 如果函数没有参数列表,那么小括号可以省略,调用时小括号必须省略
//9、如果不关心名称,只关心逻辑处理,那么函数名(def)可以省略
def f9(name:String):Unit={
println(name)
}
//匿名函数,或者说lanbda表达式,js里面叫箭头函数 本质都是定义了一个没有名字的函数
(name:String)=>{println(name)} //箭头函数,可以不要函数名,只需要有参数和函数体
}
}
五、匿名函数
1、匿名函数的定义
没有名字的函数就是匿名函数
(x:Int)=>{函数体}
x:表示输入参数类型,Int:表示输入参数类型,函数体:表示具体代码逻辑
第一种方法:
(name:String) => {println(name)}
匿名函数没有名称怎么进行调用呢,第一种方法把他赋值给一个变量 val hanshu = (name:String) => {println(name)}
直接直接输出这个变量就行了
第二种方法:
但是更多的使用,把他当做一个参数传入给其他的函数,这就非常强大,相当于普通函数里的那个待传入的参数就是,上面赋值匿名函数的那个hanshu
然后调用普通函数的时候,直接把这个匿名函数当成参数传入进去就行了,要注意,要把匿名函数当成参数传入,要注意普通函数的参数的类型还有返回值类型,要和匿名函数一样,怎么看匿名函数的返回值呢,有一个.var
方法,就可以直接看到 String=>Unit
,然后下面参数里面定义和这个一样就可以了
2、匿名函数的简化原则
1)基本说明
- 参数的类型可以省略,会根据行参做自动的推导
- 类型省略之后,发现只有一个参数,则原括号可以省略,其他情况:没有参数和参数超过1的永远不能省略圆括号
- 匿名函数如果只有一行,则大括号也可以省略
- 如果参数只出现一次,则参数省略且后面参数可以用_代替
总结:普通函数调用匿名函数的时候,如果只有一个参数,那么匿名函数的参数的类型是可以省略的,会根据形参作自动的推导,如果函数体只有一行那么花括号{}也可以省略,如果参数只出现一次,那么参数省略并且后面的参数可以用_代替,如果可以推断出当前传入的println是一个函数体,那么没有必要调用,直接输出println就把函数体输出出来了
2) 代码示例
package scala_xuexi01
class test19_lambda {
}
object test19_lambda{
def main(args: Array[String]): Unit = {
val f1 = (name:String) => {println(name)} //匿名函数非常强大,经常用高阶函数
f1("小明") //匿名函数没有名称该怎么调用呢,把他赋值个一个变量,作为表达式肯定有返回值,
//直接把返回值赋值给这个变量,这个返回值就是一个函数类型的返回值
//除了这种方法,更多的使用,是把它当成一个参数,传给其它另外的函数,这就非常强大
//定义一个函数,以函数作为参数输入
def f(fun:String=>Unit):Unit={
fun("小红") //这个相当于是定死了数据,然后放到不同的操作里去定义,这个fun就是函数参数,
} //传入进来的参数就是匿名函数,或者是赋值给变量的匿名函数
f(f1) //这是调用了上面那个函数,参数的类型和返回值一定要和上面哪个函数一样,然后调用函数就是传入那个匿名函数
f((name:String) => {println(name)}) //或者可以直接传入这个匿名函数
//匿名函数的简化原则
//1. 参数的类型可以省略,会根据行参做自动的推导
f(name=>{println(name)}) //f普通函数,调用这个匿名函数,只有一个参数这个参数的类型可以省略,不用写出来,
//2. 类型省略之后,发现只有一个参数,则原括号可以省略,其他情况:没有参数和参数超过1的永远不能省略圆括号
//3. 匿名函数如果只有一行,则大括号也可以省略
f(name => println(name)) //函数体只有一行,那么{}也可以省略
//4. 如果参数只出现一次,则参数省略且后面参数可以用_代替
//f( _ => println(_))
f(println(_)) //如果参数只出现一次,连参数名称都可以不写,直接_代替
//5. 如果可以推断出当前传入的println是一个函数体,而不是调用语句,可以直接省略下划线
f(println) //如果想传入的是一个函数体,那么没有必要调用,直接输出println就把函数体输出出来了
}
}
3、匿名函数的说明
六、函数高级
1、高阶函数
在Scala中,函数所示一等公民,怎么体现的呢?
对于一个函数,我们可以:定义函数,调用函数
package scala_xuexi01
class test {
}
object test{
def main(args: Array[String]): Unit = {
hanshu() //调用函数
}
def hanshu():Unit={ //定义函数
println("这是一个没有参数的函数")
}
}
但是其实函数更高阶的用法
1) 函数可以作为值进行传递
不仅可以吧函数赋值给常量进行调用,也可以把函数本身赋值给常量 那个函数名不要(),就函数名空一格然后 _ 就可以了val f1 = f _
这种是不想写常量的类型和返回值类型。,或者不想这么写,那把常量的类型和返回值类型都写出来,那么也可以val f2:Int=>Int = f
package scala_xuexi01
//高阶函数
class test20_gaojiehanshu {
}
object test20_gaojiehanshu{
def main(args: Array[String]): Unit = {
def f(n:Int):Int={
println("调用")
n+1
}
val result = f(123) //基本的函数声明调用,当做值赋值给一个变量
println(result)
//1.函数可以作为值进行传递
val f1 = f _ //直接输出这个函数,把函数本身赋值给这个变量,而不是调用函数
val f2:Int=>Int = f //如果没有省变量的类型和返回值的话,那么可以直接_ 都不要直接一个f
println(f1) //可以直接传入函数本身,
println(f1(12)) //也可以调用函数
println(f2)
}
}
2) 函数可以作为参数进行传递
三种输出都是可以的,第一种是有三个参数,第一个参数是函数,函数里面有两个参数,相当于调用的时候,写一个一样的数值类型和返回值的函数放到第一个参数的位置上就可以了,这个函数只是逻辑操作,后面两个参数是具体的值。第二种方法,直接把匿名函数作为第一个参数放进去。第三种方法直接用两个_
通配符代替第一个函数参数,因为要的只是逻辑操作,调不调用都是一样的
//2.函数作为参数进行传递
//定义二元计算函数
def dualEval(op:(Int,Int)=>Int,a:Int,b:Int):Int={
op(a,b) //相当于里面三个参数,一个参数a,一个参数b,还有一个函数op也作为一个参数,这个函数里也有两个参数
} //两个Int类型的参数,得到一个Int类型的返回值
def add(a:Int,b:Int):Int={ //相当于这个普通函数就是dualEval里面的op
a+b
}
println(dualEval(add,69,63)) //这个op/add 就只是进行逻辑操作,后面的a和b参数才是具体的值
println(dualEval((a,b)=>a+b,69,63)) //这样写也是一样的,本质就是第一个参数是一个函数只需要逻辑,后面两个参数进行相加
println(dualEval(_+_,69,63)) //或者直接两个_ 代替那个参数也是一样的
}
3) 函数可以作为函数的返回值返回
相当于把f6()函数,写在f5()函数的里面,注意f5返回值的类型是一个函数类型的
,这个返回值的类型首先是f6返回这个f6函数本身
,然后第二个返回值是f6
函数的返回值
,就 def f5():Int=>Unit
就这样写就可以了,然后调用的时候需要用一个常量去接收val f7 = f5()
然后去输出这个常量,或者直接输出println(f5()(25))
相当于直接调用f5()
函数,还有里面的f6()
函数
//3.函数作为函数的返回值返回
def f5():Int=>Unit ={
def f6(a:Int):Unit={ //想把函数作为返回值,那在内部再定义一个函数
println("f6调用"+a)
}
f6 //将函数直接返回
}
// println(f5()) //因为返回的是一个函数,所以这里显示的是一个引用
// val f7 = f5() //在定义一个f7用来接收f5那么就可以进行输出了
// println(f7(25))
//或者
println(f5()(25)) //直接这样,相当于二次调用 效果跟用变量接收是一样的
}
2、高阶函数案例
注意:函数里面嵌套函数,这样写,要第一个函数的返回值就是就是下一个函数的函数参数的类型,然后用=>
把下一个函数的输出的返回值类型给标识出来,这样三个函数进行嵌套,相当于函数作为了函数的返回值
需求:模拟Map映射,Filter过滤,Reduce聚合
package Scala01_xuexi
import scala.math.Equiv
class test22_hanshuzhuzu {
}
//练习2:定义一个函数func,它接收一个Int类型的函数,返回一个函数(记作f1)
//他返回的函数f1,接收一个String类型的参数,同样返回一个函数(记作F2),
//函数2接收一个Char类型的参数,返回一个Boolean的值
//要求调用函数func(0)("")('0')得到的返回值为false,其它情况均返回true
object test22_hanshuzhuzu{
def main(args: Array[String]): Unit = {
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
}
println(func(0)("")('0'))
}
}
下面是上面这段函数的简写,简化为匿名函数
然后一大堆函数就变为一行了,匿名函数不需要函数名,也不需要返回值的类型,然后当函数体只有一行的时候,那么默认就是函数的返回值,还有第一行已经把他们的输入的类型和返回值的类型都写出来了,下面的也不用写了,然后匿名函数{}也可以省,那到最后就剩下一行了
def funb(i:Int):String=>(Char=>Boolean)={
//直接写成匿名函数,匿名函数不需要函数名,返回值类型,把=改成=>
s=>c=> if(i==0&& s =="" && c=='0')false else true //然后第一行指明了后面的返回值的类型,连参数类型和返回值类型否省了
} //到最后甚至能变成这么一行,直接看不懂,反正就是能省就省
println(funb(0)("")('0')) //只有这三个条件同时满足才为False
println(funb(2)("")('0'))
下面还有一种,函数的柯里化,跟推荐使用这种,比较容易看懂一点,匿名函数都不用写了
//柯里化
//这个更流弊,匿名函数是第一行以返回值表示出来他们的输入的参数的类型,这个是直接括号表示出来,然后最后一个参数的返回值作为返回值
def func(i:Int)(s:String)(c:Char):Boolean={ //然后函数体力一个判断语句
if(i==0&& s =="" && c=='0')false else true
}
println(func(0)("")('0')) //只有这三个条件同时满足才为False
}
3、函数柯里化&闭包(重点)
闭包:函数式编程的标配
(1)、基本说明
闭包
: 如果一个函数,访问到了它的外部(局部)变量的值,那么这个函数和他所处的环境,称为闭包
函数柯里化
:把一个参数列表的多个参数,变成多个参数列表
(2)、闭包代码示例
比如两个数进行相加,然而有很多条数据,大多数都是相同的,所以可以固定一个值,然后值传入另外一个参数就可以了,就是相当于可以在外面调用局部变量,可以把函数作为返回值进行调用,这个样子挺好用的,这个函数写好之后,也可以创建一个常量进行接收,重写,重新传入参数
package Scala01_xuexi
import scala.io.StdIn
//函数闭包
class test23_hanshubibao {
}
object test23_hanshubibao{
def main(args: Array[String]): Unit = {
/*
* 函数闭包的意思是用在外部就可以调用局部变量
* */
def add(a:Int,b:Int): Int ={
return a + b
}
add(5,6)
//1,考虑固定一个加数的场景 比如有20000条要进行计算的数据,但是19000多条都是一样的,
//大多数都是一样的,那么就可以固定一个值,只传另外一个值就行了
def addByFour(b:Int): Int ={
4+b
}
addByFour(5)
//2,扩展固定加数改变的情况
//就是那个固定的数,也有可能是一些其他的数,所以得让这个固定的数可以有改变
println("====================")
def addByFive(b:Int):Int ={
5 + b
}
//3,将固定参数作为另一个参数传入,但是是作为"第一层参数"传入
def addBySix(a:Int):Int=>Int={
val a = 4
def addB(b:Int):Int={ //相当于第一个参数是已经写死了的,然后第二个函数是作为第一个函数的返回值进行返回
a + b
}
addB
}
println(addBySix(4)(5))
println(addBySix(4)(6))
println("===================")
def addBySeven(a:Int)(b:Int):Int={ //柯里化,和上面写两层函数是一样的
4 + b
}
println(addBySeven(4)(6))
println(addBySeven(4)(7))
val addc = addBySeven(8)(16) //可以直接创建一个新的对象,然后给他写入值,
println(addc)
//lambda 表达式语句
def addByA(a:Int):Int=>Int={
b=>a+b //上面都已经定义了参数的类型和返回值的类型,所以下面不用定义
}
println(addByA(6)(8))
}
}
(3)、函数柯里化代码示例
还是一样的,但是这样两个括号,跟调用的时候是一样的形式,非常容易让人看懂,柯里化和闭包是完全绑定在一起的,一旦用到了这个这个柯里化,那么它的底层一定是闭包,当然了闭包不一定非要用柯里化去书写,但是推荐使用柯里化
package Scala01_xuexi
//函数柯里化
class test24_hanshukelihua {
}
object test24_hanshukelihua{
def main(args: Array[String]): Unit = {
// 柯里化 这样写到一起非常容易看懂,
def addkeli(a:Int)(b:Int):Int={
a+b
}
println(addkeli(5)(6))
}
}
4、递归
1)说明:一个函数方法在函数/方法体内有调用了本身,我们称之为递归调用
递归算法
:
//1、必须调用自身
//2、方法必须要有跳出的逻辑
//3、方法调用自身时,传递的参数应该有规律
//3、scala 中的递归必须声明函数返回值类型
2)案例实操
尾递归优化可以解决递归耗空间的缺点,
package Scala01_xuexi
//函数递归
class test25_hanshudigui {
}
object test25_hanshudigui{
def main(args: Array[String]): Unit = {
//函数递归案例,阶乘
//递归算法:
//1、必须调用自身
//2、方法必须要有跳出的逻辑
//3、方法调用自身时,传递的参数应该有规律
//3、scala 中的递归必须声明函数返回值类型
println("请输入你想求的递归的数字:"+digui(5))
println(digui2(5))
}
//递归实现阶乘计算
def digui(n:Int):Int={ //只需要一个类型的参数,当然scala里面是需要返回值的
if (n==1){
1
}else{
return digui(n-1) * n
}
}
//尾递归实现,解决递归耗内存的这个缺点,
//思路就是本来是(n-1)*n 然后,定义两层函数,第一层式n-1,然后第二层是*n,然后n-1,的值都用一个常量result保存接收
def digui2(n:Int):Int= {
def digui3(n: Int, result: Int): Int = {
if (n == 0) {
return result //因为把当前的结果都返回给这个result 所以不返回1,返回这个result
} else {
digui3(n - 1, result * n)
}
}
digui3(n, 1)
}
}
5、抽象控制
1、值调用:把计算后的值传递过去
2、名调用:把代码传递过去
注意:值调用和名调用的区分的一个显著的区别是定义参数的时候名调用是 a: => Int 代码的是代码块的返回值是Int类型的,而值传递就是正常的怎么定义参数类型就怎么来
package Scala01_xuexi
import scala.collection.immutable.Range
//控制抽象
class test26_kongzhichouxiang {
}
object test26_kongzhichouxiang{
def main(args: Array[String]): Unit = {
//1、值调用:把计算后的值传递过去
def f0(a:Int):Unit={
println("hallo scala" + a)
println("a: " + a)
}
f0(23)
def f1():Int={
println("f1调用")
12 //他是先调用这个 "f1调用" 然后12作为返回值给f0的参数a,就变成了 "hello scala12" "a: 12"
}
f0(f1())
//2、名调用:把代码传递过去
//这个传的就不是具体的值了,他传的是一部分代码
println("=======================")
def f2(a: =>Int):Unit={ //a: =>Int代表的是一段代码块,代码块的返回值是Int
println("hallo scala" + a)
println("a: " + a)
}
f2(23)
f2(f1()) //这个f1调用几次,由a调用了几次决定的
f2({ //调用的时候传一个代码块都可以,这个29会作为返回值传给a,然后a调用两次,这个代码块就会调用两次
println("这是一个代码块")
29
})
}
}
6、用scala代码实现while关键字小案例
实现思路:用闭包将内层函数作为参数返回值返回给外层的函数,相当于有两部分,第一部分是条件判断的那部分是Boolen类型的,第二个部分是函数体的那个部分,while循环的函数体是没有返回值的所以参数的类型设置为Unit 返回值也是Unit 然后就行条件判断,如果成立,执行内层函数也就是函数体然后调用自身,因为while循环是有循环的功能,scala里面可以用递归来做到,所以带调用自身,最后调用这个内层函数,把他当做返回值返回给外层函数,就完成了,然后调用就跟while循环是一样的,第一个参数是进行条件的判断,第二个参数就是那个函数体,下面我写了四种实现的方式,除了原本的while循环,还有闭包的方式,把内层函数作为返回值返回给外层函数,然后匿名函数的方式,代码要简便一点,最后是柯里化的方式,代码更简洁了,也比较容易理解,比较推荐使用柯里化的方式
package Scala01_xuexi
class test2 {
}
object test2{
def main(args: Array[String]): Unit = {
//常规的while 循环
var n = 10
while (n >= 1){
println(s"现在是第${n}圈")
n-=1 //要有这么个终止的条件,不然就成死循环了
}
//使用自定义的函数实现while关键字的功能
//首先其实就是两个部分,一个是上面的条件判断的部分,还有就是函数体 相当于就是分开传入的两个参数
//相当于可以直接用闭包,还有传名参数,得传代码块 还有用递归实现循环的功能
println("=====================")
def MyWhile(tiaojian: =>Boolean): (=>Unit)=>Unit ={ //这个相当于是判断条件 这里之所以要加=> 是因为这是传名参数,传名参数就是这样的
//内层函数,需要递归调用,参数就是循环体
def doloop(op: =>Unit):Unit={ //这个函数相当于就是函数体
if(tiaojian){
op
MyWhile(tiaojian)(op) //这是讲函数作为参数返回值返回
}
}
doloop _
}
n = 10
MyWhile(n>=1){
println(s"现在是第${n}圈")
n-=1
}
//匿名函数实现
println("=====================")
def MyWhile2(tiaojian: =>Boolean): (=>Unit)=>Unit ={ //这个相当于是判断条件 这里之所以要加=> 是因为这是传名参数,传名参数就是这样的
//内层函数,需要递归调用,参数就是循环体
op =>{ //这个函数相当于就是函数体
if(tiaojian){
op
MyWhile2(tiaojian)(op) //这是讲函数作为参数返回值返回
}
}
}
n=10
MyWhile2(n>=1){
println(s"现在是第${n}圈")
n-=1
}
//柯里化实现
println("=====================")
def MyWhile3(tiaojian: =>Boolean)(op: =>Unit): Unit={ //这个相当于是判断条件 这里之所以要加=> 是因为这是传名参数,传名参数就是这样的
//内层函数,需要递归调用,参数就是循环体
if(tiaojian){
op
MyWhile3(tiaojian)(op)
}
}
n = 10
MyWhile3(n>=1){
println(s"现在是第${n}圈")
n-=1
}
}
}
7、惰性加载
1、说明:
当函数返回值被声明lazy时,函数的执行将被推迟,直到我们首次对此取值,该函数才会执行,这种函数我们称之为惰性函数 相当于会最后调用那个带参数的
lazy
关键字,定义
package Scala01_xuexi
//惰性加载
class test28_duoxingjiazai {
}
object test28_duoxingjiazai{
def main(args: Array[String]): Unit = {
//当函数返回值被声明lazy时,函数的执行将被推迟,直到我们首次对此取值,该函数才会执行,
//这种函数我们称之为惰性函数
lazy val result:Int = sum(13,47)
println("1、函数调用")
println("2、result = " + result) //这个2是最晚被调用的,因为是等他被调用的时候才会执行,
println("4、result = " + result) //不会再调用一次3,因为他们是最后调用才会执行的
}
def sum(a:Int,b:Int):Int={
println("3、sum调用")
a+b
}
}