- 面向对象编程
解决问题,分解对象,行为,属性,然后通过对象的关系以及行为的调用来解决问题
- 函数式编程
解决问题时,将问题分解成一个一个的步骤,将每个步骤进行封装(函数)。
严格的函数式语言是没有变量的赋值行为,讲究的是引用透明性,也就是说一个表达式返回一个值,那么它永远返回一个值,不会变。 函数式语言常常和递归联系起来,这是因为一般的循环结构,除非 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:wyand18wyand18
函数至简原则
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+bval del = (a:Int,b:Int) => a-bprintln(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、f2val f1 = f _val f2: Int=>Int = fprintln(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}// 调用f6val f6 = f5()println(f5)println(f6(25))// 或println(f5()(25))
对数组进行处理,将操作对象抽象出来,处理完毕之后将结果返回给一个新的数组
def main(args: Array[String]): Unit = {// mapval 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 1fact(a-1) * a}println(fact(5))
尾递归实现
- 使用loop函数取出之前的有效值,舍去无用的数值
- @tailrec指明该函数为尾递归,用来检查该方法是否为尾递归函数
- 在Java中不可以,因为这样的实现需要编译器支持
def tailfact(n: Int): Int = {@tailrecdef 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=24f1调用a=24
自定义while循环
先看一看使用while循环的场景
var num = 12while(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){opmyWhile(condition)(op)}}doLoop _}myWhile(num >= 1){println(num)num-=1}
代码简化:
def myWhile(condition: => Boolean): (=> Unit) => Unit = {op => {if (condition) {opmyWhile(condition)(op)}}}
继续简化(柯里化):
def myWhile(condition: => Boolean)(op: =>Unit): Unit = {if (condition) {opmyWhile(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
