- 面向对象编程
解决问题,分解对象,行为,属性,然后通过对象的关系以及行为的调用来解决问题
- 函数式编程
解决问题时,将问题分解成一个一个的步骤,将每个步骤进行封装(函数)。
严格的函数式语言是没有变量的赋值行为,讲究的是引用透明性,也就是说一个表达式返回一个值,那么它永远返回一个值,不会变。 函数式语言常常和递归联系起来,这是因为一般的循环结构,除非 while(1) 这种死循环,都是和表达式的变动关联起来的,比如说 while(n) 就是要不断修改 n 的值直到 n == 0,在函数式语言中是必须避免的。 递归通过调用函数的参数不同,来达到数据的变动却不破坏引用透明性。并且如果加入尾递归优化,那么递归的性能和循环是等价的。 函数式语言另外一个特点,也是一个语言能自称函数式的关键所在是,函数是“一等公民”,这是说能像操作数据一样在函数中动态生成新的函数,可以将函数赋值给变量,可以把函数放到数据结构里,可以把函数作为参数和返回值。 达到这一个特性的语言都可以宽泛的称作函数式编程语言,并不和过程式和面向对象冲突。
函数基本语法
函数和方法的区别
1) 核心概念
(1)为完成某一功能的程序语句的集合,称为函数。
(2)类中的函数称之方法。
2) 案例实操
(1 ) Scala语言可以在任何的语法结构中声明任何的语法
(2)函数没有重载和重写的概念;方法可以进行重载和重写
(3 ) Scala中函数可以嵌套定义
函数定义
(1) 函数1:无参,无返回值
(2) 函数2:无参,有返回值
(3) 函数3:有参,无返回值
(4) 函数4:有参,有返回值
(5)函数5:多参,无返回值
(6)函数6:多参,有返回值
def f1(): Unit={
println("无参无返回值")
}
def f2():Int={
println("无参,有返回值")
return 12
}
def f3(name: String):Unit={
println("有参,无返回值")
}
def f4(name: String):String={
println("有参,有返回值")
return "hi "+ name
}
def f5(name1:String,name2:String):String={
println("多参,有返回值")
return name1 + name2
}
def f6(name1:String,name2:String):Unit={
println("多参,有返回值")
}
函数参数
- 可变参数
- 如果参数列表中存在多个参数,那么可变参数一般放置在最后
- 参数默认值,一般将有默认值的参数放置在参数列表的后面
- 带名参数
可变参数
def function1(str:String*):Unit={
println(str)
}
function1("a")
function1("a","b","c")
//result:
WrappedArray(a)
WrappedArray(a, b, c)
带名参数
含有大量默认值时候,不依靠原有的顺序去指定参数,非常的方便
def function2(name:String,age:Int):Unit={
println(s"${name}and$age")
}
function2("wy",18)
function2(age = 18,name = "wy")
}
//result:
wyand18
wyand18
函数至简原则
1.return可以省略,Scala会使用函数体的最后一行代码作为返回值
2.如果函数体只有一行代码,可以省略花括号
3.返回值类型如果能够推断出来,那么可以省略(:和返回值类型一起省略)
4.如果有return,则不能省略返回值类型,必须指定
5.如果函数明确声明unit,那么即使函数体中使用return关键字也不起作用
6.Scala如果期望是无返回值类型,可以省略等号。这种形式称为过程
7.如果函数无参,但是声明了参数列表,那么调用时,小括号,可加可不加
8.如果函数没有参数列表,那么小括号可以省略,调用时小括号必须省略
9.如果不关心名称,只关心逻辑处理,那么函数名(def)可以省略(匿名函数)
匿名函数
没有名字的函数
(x:Int) => {函数体}
val fun = (name:String) => {println(name)}
fun("haha")
def f(func: String => Unit):Unit={
func("rabbit")
}
f(fun)
匿名对象
f((name:String) => {println(name)})
匿名函数省略原则
(1)参数的类型可以省略,会根据形参进行自动的推导;
f((name) => {println(name)})
(2)类型省略之后,发现只有一个参数,则圆括号可以省略;f(name => {println(name)})
其他情况:没有参数和参数超过1的永远不能省略圆括号。
(3)匿名函数如果只有一行,则大括号也可以省略;f(name => println(name))
(4)如果参数只出现一次,则参数省略且后面参数可以用代替。
`f(println())<br />(5)如果可以推断出,当前传入的println是一个函数体,而不是调用语句,可以直接省略下划<br />
f(println)`
举例:
def run(fun:(Int,Int)=>Int):Int = {
fun(1,2)
}
val add = (a:Int,b:Int) => a+b
val del = (a:Int,b:Int) => a-b
println(run(add))
println(run(del))
//result:
3
-1
匿名函数可以继续简化println(run(_+_))
println(run(_-_))
println(run(_+_))
println(run(_-_))
println(run(-_-_))
println(run(+_-_))
//result:
3
-1
-3
-1
高阶函数
常见方式
def f(n:Int):Int={
println("f调用")
n + 1
}
val result: Int =f(123)
println(result)
函数作为值进行传递
// 把完整的函数传递给f1、f2
val f1 = f _
val f2: Int=>Int = f
println(f1(12))
println(f2(12))
println(f1)
println(f2)
// 本质上是两个不同的引用,也说明了Scala也是面向对象编程
//f调用
//13
//f调用
//13
// Demo01.demo2$$$Lambda$5/6738746@7e0babb1
// Demo01.demo2$$$Lambda$6/2096171631@6debcae2
函数作为参数进行传递
def num(op:(Int,Int)=>Int,a:Int,b:Int):Int={
op(a,b)
}
def add(a:Int,b:Int):Int ={
a+b
}
println(num(add,12,13))
println(num((a,b)=> a+b,12,13))
println(num(_ + _,12,13))
函数作为函数的返回值返回
def f5():Int=>Unit={
def f6(a: Int):Unit={
println("f5调用"+a)
}
f6
}
// 调用f6
val f6 = f5()
println(f5)
println(f6(25))
// 或
println(f5()(25))
对数组进行处理,将操作对象抽象出来,处理完毕之后将结果返回给一个新的数组
def main(args: Array[String]): Unit = {
// map
val arr:Array[Int] = Array(12,13,14,15)
// 对数组进行处理,将操作对象抽象出来,处理完毕之后将结果返回给一个新的数组
def array(array:Array[Int],op:Int => Int):Array[Int]={
for (elem <- array) yield op(elem)
}
/**
* 要注意的是,接收返回结果的是一个新的数组,op是一个操作,使用for循环对旧数组进行遍历
*/
def addOne(elem:Int):Int={elem+1}
val newArray:Array[Int] = array(arr,addOne)
println(newArray.mkString(","))
//传入匿名函数,实现元素翻倍
val newArray2 = array(arr,_*2)
println(newArray2.mkString(","))
}
题目测试
涉及闭包和柯里化
def main(args: Array[String]): Unit = {
def fun =(a:Int,b:String,c:Char)=>{
if (a==0&&b==""&&c=='0') {false}
else {true}
}
println(fun(1,"1",1))
def func(i: Int):String => (Char=>Boolean) ={
def f1(i1:String):Char=>Boolean = {
def f2(i2:Char):Boolean = {
if (i==0&&i1==""&&i2=='0') {false}
else {true}
}
f2
}
f1
}
def func1(i: Int):String =>(Char=>Boolean) ={
i1=> i2=> if (i==0&&i1==""&&i2=='0') false else true
}
//柯里化
def func2(i: Int)(i1:String)(i2:Char):Boolean={
if (i==0&&i1==""&&i2=='0') false else true
}
println(func1(0)("")('0'))
}
函数柯里化&闭包
闭包:函数式编程的标配
1)说明
- 闭包: 如果一个函数,访问到了它的外部(局部)变量的值,那么这个函数和他所处的环境,称为闭包
- 函数柯里化: 把一个参数列表的多个参数,变成多个参数列表。
在Scala语言中,所有的函数都是一个对象实例,放在堆中。使用对象都是对对象的引用。
闭包:
通用性好,适用性差
def add(a:Int):Int =>Int={
def addB(b:Int):Int={
a+b
}
addB
}
println(add(12)(12))
val addby1 = add(1)
val addby2 = add(1)
println(addby1(12))
println(addby2(15))
优化代码:
def add(a: Int): Int => Int = {b => a + b}
def add(a: Int): Int => Int = a + _
柯里化:
一旦用到了柯里化,必定用到了闭包,因为柯里化的底层就是闭包
递归
一个函数/方法在函数/方法体内又调用了本身,我们称之为递归调用
举例(阶乘):
def fact(a:Int): Int ={
if (a==0) return 1
fact(a-1) * a
}
println(fact(5))
尾递归实现
- 使用loop函数取出之前的有效值,舍去无用的数值
- @tailrec指明该函数为尾递归,用来检查该方法是否为尾递归函数
- 在Java中不可以,因为这样的实现需要编译器支持
def tailfact(n: Int): Int = {
@tailrec
def loop(n: Int, currRes: Int): Int = {
if (n == 0) {return currRes}
loop(n - 1, currRes * n)
}
loop(n, 1)
}
println(tailfact(5))
控制抽象
针对函数的参数而言,是函数参数中的一个特性。
- 值调用
把计算后的值传递过去
def f0(a:Int):Unit={
println("a:"+ a)
}
def f1():Int ={
24
}
f0(f1())
- 名调用
把代码传递过去
def f1():Int ={
println("f1调用")
24
}
def f2(a: =>Int):Unit ={
println("a="+a)
println("a="+a)
}
f2(f1())
//result:
f1调用
a=24
f1调用
a=24
自定义while循环
先看一看使用while循环的场景
var num = 12
while(num >= 1){
println(num)
num-=1
}
step1:传递num >= 1
step2:传递println(num)
实现就可以用传名调用
var num = 12
//用匿名函数实现
def myWhile(condition: =>Boolean): (=>Unit)=>Unit={
//内层函数需要递归调用,参数就是循环体
def doLoop(op: =>Unit):Unit={
if(condition){
op
myWhile(condition)(op)
}
}
doLoop _
}
myWhile(num >= 1){
println(num)
num-=1
}
代码简化:
def myWhile(condition: => Boolean): (=> Unit) => Unit = {
op => {
if (condition) {
op
myWhile(condition)(op)
}
}
}
继续简化(柯里化):
def myWhile(condition: => Boolean)(op: =>Unit): Unit = {
if (condition) {
op
myWhile(condition)(op)
}
}
惰性加载
当函数返回值被声明为lazy
时,函数的执行将被推迟,直到我们首次对此取值,该函数才会执行。这种函数我们称之为惰性函数。
//sum被使用时才被调用
lazy val result :Int = sum(12,47)
println("1.函数调用")
println("2.result:"+result)
}
def sum(a: Int, b: Int):Int={
println("3.sum调用")
a + b
}
1.函数调用
3.sum调用
2.result:59