ScalaNote22-函数式高级编程

网友投稿 800 2022-09-02

ScalaNote22-函数式高级编程

ScalaNote22-函数式高级编程

偏函数

先看一个需求,给你一个集合val list = List(1, 2, 3, 4, “abc”) ,请完成如下要求: 将集合list中的所有数字+1,并返回一个新的集合 要求忽略掉 非数字 的元素,即返回的 新的集合 形式为 (2, 3, 4, 5)

用之前学过的知识,这个题有两个解法,第一个是利用filter和map函数

filter筛选出Int的元素,注意此时返回的Any类型把filter之后的元素再转换成Int类型map对筛选出来的元素计算+1

val list = List(1, 2, 3, 4, "abc") def isInt(x:Any)={ // 返回true false x.isInstanceOf[Int]}def asInt(x:Any)={ // 返回true false x.asInstanceOf[Int]}def andOne(x:Int)={ x+1}// 当然asInt和andOne可以写在一起,这样省点事list.filter(isInt).map(asInt).map(andOne)

list: List[Any] = List(1, 2, 3, 4, abc)isInt: (x: Any)BooleanasInt: (x: Any)IntandOne: (x: Int)Intres3: List[Int] = List(2, 3, 4, 5)

也可以用模式匹配写

// 此时如果x非Int,返回(),所以还得再filterdef matchF(x:Any)={ x match{ case x:Int => x+1 case _ => }}println(list.map(matchF))println(list.map(matchF).filter(isInt))

List(2, 3, 4, 5, ())List(2, 3, 4, 5)matchF: (x: Any)AnyVal

上述两种方法都要进行filter和map两步操作,偏函数可以一步到位~

在对符合某个条件,而不是所有情况进行逻辑操作时,使用偏函数是一个不错的选择将包在大括号内的一组case语句封装为函数,我们称之为偏函数,它只对会作用于指定类型的参数或指定范围值的参数实施计算,超出范围的值会忽略(未必会忽略,这取决于你打算怎样处理)偏函数在Scala中是一个特质

直接上代码吧~

val list = List(1, 2, 3, 4, "abc")//定义一个偏函数 //1. PartialFunction[Any,Int] 表示偏函数接收的参数类型是Any,返回类型是Int //2. isDefinedAt(x: Any) 如果返回true ,就会去调用 apply 构建对象实例,如果是false,过滤 //3. apply 构造器 ,对传入的值 + 1,并返回(新的集合) //4. isDefinedAt和apply方法是固定的,不可以随便定义方法名称val addOne3= new PartialFunction[Any, Int] {def isDefinedAt(any: Any) = if (any.isInstanceOf[Int]) true else falsedef apply(any: Any) = any.asInstanceOf[Int] + 1}val list3 = list.collect(addOne3)println("list3=" + list3)

list3=List(2, 3, 4, 5)list: List[Any] = List(1, 2, 3, 4, abc)addOne3: PartialFunction[Any,Int] = list3: List[Int] = List(2, 3, 4, 5)

使用构建特质的实现类(使用的方式是PartialFunction的匿名子类)PartialFunction 是个特质(看源码)构建偏函数时,参数形式[Any, Int]是泛型,第一个表示参数类型,第二个表示返回参数当使用偏函数时,会遍历集合的所有元素,编译器执行流程时先执行isDefinedAt()如果为true ,就会执行 apply, 构建一个新的Int对象返回执行isDefinedAt() 为false 就过滤掉这个元素,即不构建新的Int对象map函数不支持偏函数,因为map底层的机制就是所有循环遍历,无法过滤处理原来集合的元素collect函数支持偏函数

上面偏函数也有其他简化形式,后面遇到我应该也不会写了吧。。。直接把代码Copy过来~

def f2: PartialFunction[Any, Int] = { case i: Int => i + 1 // case语句可以自动转换为偏函数}List(1, 2, 3, 4,"ABC").collect(f2)

f2: PartialFunction[Any,Int]res10: List[Int] = List(2, 3, 4, 5)

List(1, 2, 3, 4,"ABC").collect{ case i: Int => i + 1 case i:String=>i*2}

res13: List[Any] = List(2, 3, 4, 5, ABCABC)

作为参数的函数

这个其实在map中用过,简单看个例子:

//说明def plus(x: Int) = 3 + x//说明println(Array(1, 2, 3, 4).map(plus(_)).mkString(","))println(Array(1, 2, 3, 4).map(plus).mkString(","))

4,5,6,74,5,6,7plus: (x: Int)Int

​​map(plus(_)) 中的plus(_) 就是将plus这个函数当做一个参数传给了map,_这里代表从集合中遍历出来的一个元素​​​​plus(_) 这里也可以写成 plus 表示对 Array(1,2,3,4) 遍历,将每次遍历的元素传给plus的 x!​​

匿名函数

没有名字的函数就是匿名函数,可以通过函数表达式来设置匿名函数!

val triple = (x: Double) => 3 * xprintln(triple(3))println(triple.getClass)

9.0class $line39.$read$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$anonfun$1triple: Double => Double =

(x: Double) => 3 * x 就是匿名函数(x: Double) 是形参列表, => 是规定语法表示后面是函数体, 3 * x 就是函数体,如果有多行,可以 {} 换行写triple 是指向匿名函数的变量

再看个例子:

val f1 = (n1: Int, n2: Int ) => {println("biubiubiu")n1 + n2}println(f1.getClass)println("f1:" + f1)println(f1(10, 30))

class $line41.$read$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$anonfun$1f1:biubiubiu40f1: (Int, Int) => Int =

高阶函数

能够接受函数作为参数的函数,叫做高阶函数 (higher-order function)。可使应用程序更加健壮

//test 就是一个高阶函数,它可以接收f: Double => Double ,f的参数和返回值都为Doubledef test(f: Double => Double, n1: Double) = {f(n1)}//sum 是接收一个Double,返回一个Doubledef sum(d: Double): Double = {d + d}val res = test(sum, 6.0)println("res=" + res)

res=12.0test: (f: Double => Double, n1: Double)Doublesum: (d: Double)Doubleres: Double = 12.0

上面的例子是把函数作为参数,我们也可以定义一个函数,使之结果返回为一个函数

// 相当于把x固定了返回的新函数中,y是参数def minusxy(x: Int) = { (y: Int) => x-y //匿名函数}//这一步相当于先返回个函数(3-y),然后y=5val result3 = minusxy(3)(5) // println(result3)

minusxy: (x: Int)Int => Intresult3: Int = -2

参数类型推断

据说这里的应用很多,我暂时没遇到过。参数推断省去类型信息(在某些情况下[需要有应用场景],参数类型是可以推断出来的,如list=(1,2,3) list.map(),map中函数参数类型是可以推断的,因为list中元素都是Int),同时也可以进行相应的简写。 参数类型推断写法说明:

参数类型是可以推断时,可以省略参数类型当传入的函数,只有单个参数时,可以省去括号如果变量只在=>右边只出现一次,可以用_来代替

val list = List(1, 2, 3, 4)//匿名函数写法println(list.map((x:Int)=>x + 1)) //(2,3,4,5)//list中都是Int,可以简写println(list.map((x)=>x + 1)) //(2,3,4,5)println(list.map(x=>x + 1)) //(2,3,4,5)//x在右边只出现一次,直接_简写println(list.map( _ + 1)) //(2,3,4,5)// reduce好像默认是reduceLeft?从左边开始计算,把元素一次传进去 println(list.reduce((n1:Int ,n2:Int) => n1 + n2)) //10println(list.reduce((n1 ,n2) => n1 + n2)) //10println(list.reduce( _ + _)) //10

List(2, 3, 4, 5)List(2, 3, 4, 5)List(2, 3, 4, 5)List(2, 3, 4, 5)101010list: List[Int] = List(1, 2, 3, 4)

闭包

前面高阶函数中,返回值为函数的case已经涉及到了闭包。闭包就是一个函数和与其相关的引用环境组合的一个整体(实体)。看个例子:

//1.用等价理解方式改写 2.对象属性理解def minusxy(x: Int) = (y: Int) => x - y//f函数就是闭包.这玩意感觉就是方便后面使用时,不需要重复把x=20传进去val f = minusxy(20) println("f(1)=" + f(1)) // 19println("f(2)=" + f(2)) // 18

f(1)=19f(2)=18minusxy: (x: Int)Int => Intf: Int => Int =

再来看个实际case:

编写一个函数 makeSuffix(suffix: String) 可以接收一个文件后缀名(比如.jpg),并返回一个闭包调用闭包,可以传入一个文件名,如果该文件名没有指定的后缀(比如.jpg) ,则返回 文件名.jpg , 如果已经有.jpg后缀,则返回原文件名要求使用闭包的方式完成后缀名可以使用String.endsWith(xx),这个函数类似截取最后length(xx)个字符串和xx比较,返回true false

def makeSuffix(suffix: String) ={ (fileName:String)=> if( fileName.endsWith(suffix)) fileName else fileName+suffix}val f = makeSuffix(".jpg")println(f("dog.jpg")) // dog.jpgprintln(f("cat")) // cat.jpg

dog.jpgcat.jpgmakeSuffix: (suffix: String)String => Stringf: String => String =

函数柯里化

韩老师讲的一个例子很懵逼,有点绕,但是如果只看函数柯里化的形式,似乎又不是很复杂。

函数编程中,接受多个参数的函数都可以转化为接受单个参数的函数,这个转化过程就叫柯里化柯里化就是证明了函数只需要一个参数而已。其实我们刚才的学习过程中,已经涉及到了柯里化操作

上面闭包和高阶函数的case就用到所谓的函数柯里化

def mulCurry1(x: Int,y:Int) = x * ydef mulCurry2(x: Int)(y:Int) = x * yprintln(mulCurry1(10,8))println(mulCurry2(10)(8))

8080mulCurry1: (x: Int, y: Int)IntmulCurry2: (x: Int)(y: Int)Int

控制抽象

控制抽象学着更是蒙蔽。。。先看定义: 控制抽象是函数且满足如下条件

参数是函数函数参数没有输入值也没有返回值

比如之前学习的breakable{}函数,里面放的是while的循环体,可以把代码块当成一个函数。直接看韩老师给的case吧

var x = 10def until(condition: => Boolean)(block: => Unit): Unit = {//类似while循环,递归if (!condition) {blockuntil(condition)(block)}// println("x=" + x)// println(condition)// block// println("x=" + x)}// x==0是condition,后面的代码块是block参数//如果condition不满足,则执行block,并且递归调用untiluntil(x == 0){x -= 1println("x=" + x)}

x=9x=8x=7x=6x=5x=4x=3x=2x=1x=0x: Int = 0until: (condition: => Boolean)(block: => Unit)Unit

2020-03-17 于南京市栖霞区

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:ScalaNote21-模式匹配
下一篇:作为程序员,你是如何有效的调试bug的(程序员如何解决bug)
相关文章

 发表评论

暂时没有评论,来抢沙发吧~