Scala之函数式编程
Posted 小唐同学(๑><๑)
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Scala之函数式编程相关的知识,希望对你有一定的参考价值。
目录
函数和方法的区别:
方法和函数的区别就是在位置上的不同
方法--是在类中表示(面向对象)-----伴生对象中的方法(相当于静态方法) 或者在伴生类中定义方法
写在其他位置的都是函数
函数是支持嵌套的但是并不支持函数的重载和重写(重载和重写是针对的类中的方法而言)
Scala中也有可变参数,不过与Java不同
def main(args: Array[String]): Unit =
def sayhi(name: String*): Unit=
println(s"hi $name")
sayhi()
sayhi("shdus")
sayhi("sdsdsd","shdushd")
执行结果
参数默认值:
(Java中不存在默认值 可通过重载实现)
def main(args: Array[String]): Unit =
def sayhi(name: String="zhangsan"): Unit=
println(s"hi $name")
sayhi()
sayhi("shdus")
//sayhi("sdsdsd","shdushd")
执行结果:
当想使用默认参数时,且有多个参数时 需要使用带名参数
def main(args: Array[String]): Unit =
def sayhi(name: String="zhangsan",age :Int): Unit=
println(s"hi $name age is $age")
sayhi(age = 18)
sayhi("shdus",18)
//sayhi("sdsdsd","shdushd")
执行结果:
函数至简原则---能省则省:
至简原则细节
( 1 ) return 可以省略, Scala 会使用函数体的最后一行代码作为返回值特殊情况 return 不能省略:
//
def sayhi2(name :String) :String =
if(name==null)
return ""
println(s"Hi $name")
name
比如 传入参数为null 这个return 就不能省略 这里要作为程序的一个出口
如果省略 则会执行下边的语句 返回name
( 2 )如果函数体只有一行代码,可以省略花括号 如果不是一行代码而且省略了 ( 3 )返回值类型如果能够推断出来,那么可以省略(和返回值类型一起省略) //函数式编程最终省略的结果是 只保留传入的是什么 传出的是什么(4)如果有 return,则不能省略返回值类型,必须指定
因为每一行代码都是一个代码块 如果不写返回类型 就会返回默认最后一行代码 这时return name的返回值类型为nothing类型
def sayhi5(name :String):String=
println(s"hi $name")
return name
(
5
)
如果函数明确声明 unit,那么即使函数体中使用 return 关键字也不起作用
(6)Scala 如果期望是无返回值类型,可以省略等号
(7)如果函数无参,但是声明了参数列表,那么调用时,小括号,可加可不加
(8)如果函数没有参数列表,那么小括号可以省略,调用时小括号必须省略
(
9
)如果不关心名称,只关心逻辑处理,那么函数名(
def
)可以省略----匿名函数
(
=>前后分别是传入参数和返回值 =之前的是传入参数与返回值类型 = 之后 是传入的变量名 与实现逻辑)
(匿名函数中没有结果类型)
val function: (String, Int) => String = (name: String, age: Int) => name + "的年龄是" + age
匿名函数的化简:
匿名函数至简原则:
(1)参数的类型可以省略,会根据形参进行自动的推导可根据后边推到前面 也可根据前面推到后边 一般都是化简后边的
val function1 = (name: String, age: Int) => name + "的年龄是" + age
//后推前
val function2: (String, Int) => String = (name,age) => name + "的年龄是" + age
//前面推后边
(2)类型省略之后,发现只有一个参数,则圆括号可以省略;其他情况:没有参数和参
数超过 1 的永远不能省略圆括号。
(3)匿名函数如果只有一行,则大括号也可以省略
多行不可以省略
val function3: (String, Int) => String = (name: String, age: Int) =>
println(name + "的年龄是" + age+"666")
name + "的年龄是" + age
(4)如果(所有)参数(都)只出现一次,则参数省略且后面参数可以用_代替(但是只剩下_是避不可以的)
val function5 :(Int,Int)=>Int //输入类型 输出类型
=
2*_+4*_
高阶函数:
高阶函数的三种用法:
(1)函数可以作为值进行传递
是将这个函数当作值进行传递
val function: String => String = sayHi _
function("tangxiaocong")
(2)函数可以作为参数进行传递**
先构造一个抽象函数 再调用的时候 当作参数传递真正的逻辑
def operaXY(x:Int,y:Int,func:(Int,Int)=>Int):Int=
func(x,y)
def sumXY(x:Int,y:Int): Int =x+y
val i: Int = operaXY(10, 20, sumXY)
println(i);
不化简的匿名:
def operaXY(x:Int,y:Int,func:(Int,Int)=>Int):Int=
func(x,y)
val i: Int = operaXY(10, 20, (x:Int,y:Int)=>x-y)
println(i);
匿名化简:
(1)化简类型
(2)化简xy使用_代替
def operaXY(x:Int,y:Int,func:(Int,Int)=>Int):Int=
func(x,y)
val i: Int = operaXY(10, 20, _-_)
println(i);
(3)函数可以作为函数返回值返回***
涉及到函数的一个嵌套,一级调用返回的是一个函数 二级调用返回值
def sumxy1(x:Int)=
def sumy(y:Int): Int =
x+y
sumy _
val function1: Int => Int = sumxy1(10)
println("歇一会")
val i1: Int = function1(20)
println(i1)
使用匿名函数进行化简
将等号后边的匿名函数作为返回值进行返回
def sumxy2(x: Int): Int => Int = (y: Int) => x + y // 将等号后边的匿名函数作为返回值进行返回
val intToInt: Int => Int = sumxy2(58)
val i3: Int = intToInt(23)
println(i3)
再次对匿名函数进行化简:
这种化简就是过度化简,难懂,不建议
def sumxy3(x: Int): Int => Int = x + _
柯里化写法:
为了防止过度化简,作者引入了一个数学概念---柯里化的写法:
把多个形参的参数列表打散成一个形参的多个参数列表
def KlH(c:Char)(s:String)(i:Int)=
c !='0' || s != "" || i !=0
println(KlH('0')("")(0))
闭包:
闭包:函数式编程的标配
闭包 :如果一个函数,访问到了它的外部(局部)变量的值,那么这个函数和他所处的 环境,称为闭包(防止嵌套造成栈溢出) 闭包让代码更加灵活 (闭包其实就是把外层的变量打包成常量让内层函数使用) def sumx(x:Int)=
def sumy(y:Int):Int=
x+y
sumy _
上述采用的相当于是柯里化写法--先进行第一层调用 传入x再传递给内层函数 让x从变量变成常量,防止多次嵌套造成栈溢出
val function: Int => Int = sumx(23)
val i: Int = function(34)
println(i)
递归:
递归:嵌套调用自身的函数 递归其实是不如循环快的,因为递归还是会造成压栈
def jc(n: Int):Long =
//递归:嵌套调用自身的函数
if(n==1)
1
else
jc(n-1)*n
println(jc(5))
优化递归:尾递归优化
尾递归:最后递归调用函数的一行 只有自身的函数 没有其他值
实际尾递归是用到了闭包 递归没办法用闭包(闭包 把上一层的变量在本层变成常量,减少压栈次数----引入了res变量)
def jc1(n: Int,res:Long=1):Long =
//递归:嵌套调用自身的函数
if(n==1)
res
else
jc1(n-1,res*n)
判断是否是尾递归,可以使用一个注解----@tailrec(不是尾递归会报错)
控制抽象:
函数定义有值调用和名调用(两种方式的传参方式不同)
值调用:
--将代码块的结果作为参数传入函数中,使用参数时,只带入结果
(值调用是指函数在调用时,先计算参数的值,然后再将这个值传递给函数。这意味着每个参数都只计算一次)
名调用:
--直接将整个代码块传入参数中,使用一次参数就会运行一次
(名调用是指函数在调用时,将参数的表达式作为代码块传递给函数。这意味着每次函数使用参数时都会重新计算它的值。)
//值调用
def sayHi1(name:String):Unit=
println("函数调用")
println(s"$name hi")
println(s"$name hi")
sayHi1(
println("hello")
"linhai"
)
println("==================================================")
//名调用
def sayHi2(name: => String):Unit=
println("函数调用")
println(s"$name hi")
println(s"$name hi")
sayHi2(
println("hello")
"linhai"
)
运行结果的对比:
懒加载:
其实懒加载相当于Linux中的sleep函数 会暂时性休息,当使用到惰性语句的时候才会运行
关键字---lazy 而且lazy只能用于val关键字上
def sumxy(x:Int,y:Int):Int=
println("sumxy调用")
x+y
lazy val sum=sumxy(10,20)
println("啦啊啦啦啦")
println(sum)
运行结果:
自定义while循环:
def mywhile(b:Boolean)(op : => Unit):Unit=
if (b)
op
mywhile(b)(op)
var i=0
mywhile(i<5)(
println(i)
i +=1
)
上述代码会陷入死循环 因为传入b(i<5)的时候是值调用 只传参一次 一直为真则一直++
改进 对b进行名调用
Scala编程之惰性函数
一、为什么需要惰性函数惰性计算(尽可能延迟表达式求值)是许多函数式编程语言的特性。惰性集合在需要时提供其元素,无需预先计算它们,这带来了一些好处。首先,您可以将耗时的计算推迟到绝对需要的时候。其次,您可以创造无限个集合,只要它们继续收到请求,就会继续提供元素。函数的惰性使用让您能够得到更高效的代码。Java并没有为惰性提供原生支持, Scala提供了,使用很方便。
二、java实现懒加载
懒汉式
public class LazyDemo {
private String property; //属性也可能是一个数据库连接,文件等资源
public String getProperty(){
if (property==null){
property=initProperty(); //如果没有初始化过,那么进行初始化
}
return property;
}
private String initProperty() {
return "property";
}
}
三、惰性函数介绍
当函数被声明为lazy的时候,函数并不会立即执行,而是当我们首次对这个函数进行调用了,我们才会执行这个函数。所以我们把这个叫做惰性函数(在java中叫做懒加载)。
四、案例
代码一:
object LazyDemo {
def main(args: Array[String]): Unit = {
val res= sum(10,20) //没有lazy的修饰,这个函数就是eager的
println("================")
// println("res:"+res)
}
def sum(a:Int,b:Int):Int={
println("sum执行了")
a+b
}
}
输出: ================
sum执行了
res:30
代码二:
object LazyDemo {
def main(args: Array[String]): Unit = {
lazy val res= sum(10,20) // lazy修饰,sum()函数不会被立即执行,要首次被调用之后再执行
println("================")
// println("res:"+res)
}
def sum(a:Int,b:Int):Int={
println("sum执行了")
a+b
}
}
输出: ================
代码二:
object LazyDemo {
def main(args: Array[String]): Unit = {
lazy val res= sum(10,20) // lazy修饰,sum()函数不会被立即执行,要首次被调用之后再执行
println("================")
println("res:"+res) //调用sum方法
}
def sum(a:Int,b:Int):Int={
println("sum执行了")
a+b
}
}
输出: ================
sum执行了
res:30
综上我们可以看出,scala中被lazy修饰之后,可以实现懒加载,这在大数据项目中数据的加载计算会非常有用!!
五:注意
1)lazy不能修饰var类型变量
2)函数被lazy修饰后,会导致函数的运行被推迟,我们在声明一个变量,如果给变量加个lazy,那么变量的声明也会被推迟,只有被使用时才会声明生效。例如:
scala> val a=100
a: Int = 100
scala> lazy val b=1000
b: Int = <lazy>
scala> println(b)
1000
以上是关于Scala之函数式编程的主要内容,如果未能解决你的问题,请参考以下文章