Scala编程——第六章:Scala函数式编程高级(上)

一、作为值的函数

  • 在Scala中,函数是"头等公民",就和数字一样,你可以在变量中存放函数。
    import scala.math._
    val num = 3.14
    val fun = ceil _    //注意有个下划线_
    
    这段代码将num设为3.14,将fun设为ceil函数。
    
    你能对函数这什么呢? 两件事:
       1. 调用这个函数。
       2. 传递这个函数,存放在变量中,或者作为一个参数传递给另一个函数。
    
    调用函数
       fun(num)   普通的函数语法调,唯一的区别是: fun是一个包含函数的变量,而不是一个固定的函数。
    
    传递函数   
       Array(1.5, 2,5, 3.0).map(fun)  map接受一个函数,然后应用到数组中的所有值,然后返回结果的数组
    
    在这里插入图片描述

二、函数字面量

1.字面量

  • Scala所有的基础类型都可以用字面量来书写,字面量是在代码中直接写入常量值的一种方式。
     1.整数字面量:用于 Int Long Short Byte 的整数字面量有两种形式:十进制的 与 十六进制的
     	val hex1 = 0x5     Ox5就是整数字面量
     	val hex2 = 5       5 就是字面量
        
        整数字面量以L或者l 就是Long型的。
     	val hex3 = 35L 
     
     2.浮点数字面量: 由十进制的数字、可选的小数点,以及后续一个可选的e或E 打头的指数( exponent )组成
    	val big = 1.2345   	1.2345 就是字面量
    	val big1 = 1.245E1  1.245E1  =  12.345
    	
    	如果浮点数字面 结尾,那它就是Float 型的; 否则它就是 Double。
    	val little = 1.2345F   1.2345就是字面量 同时是Float类型的字面量。
    
    3.字符字面量: 由一对单引号和中间的任意 Unicode 字符组成。
    	val  a = 'A'   'A' 就是字面量。
    
    4. 字符串字面量:双引号包起来的字符组成
    	val hello = "hello"
    
    5. 布尔值字面量 : 只有两个字面量 truefalse
    	val bool = true
    

2.函数字面量(匿名函数)

  • Scala不定可以定义函数并调用它们,还可以用匿名的字面量来编写函数并将它们作为值进行传递。

  • 函数字面量定义方法:用圆括号括起来的一组带名字的参数、一个右箭头和函数体。 在这里插入图片描述

    以下是对某个数加1的函数字面的简单示例
    (x: Int) => x + 1 
    => 表示将左侧的内容 转换成右侧的内容。
    
    var  increase = (x: Int) => x + 1
    increase(10)    得到 11
    
    由于increase是var, 可以将它赋值成其他函数值。
    
    increase = (x: Int) => x + 99
    increase(10)    得到 109
    

    在这里插入图片描述

  • 函数字面量函数值的区别:

    • 函数字面量被编译成类,并在运行时实例化成函数值。因此,函数字面量存在于源码,而函数值以对象形式存在于运行时。(这类似于 类(源码) 与对象(运行时)的区别 )

3.函数字面量的简写

Scala提供了多个省去冗余信息的简洁编程方式。函数字面量的简写形式,主要运用参数类型推断占位符语法

(1)简写规则:

  • 当参数的类型是可以推断的类型的时候,可以省略参数类型。
  • 当传入函数的参数,只有一个的时候,可以省去圆括号。
  • 当变量在右箭头"=>"只在左边出现一次的时候,可以用占位符”_“代替
    以下是筛选出列表中大于0的函数字面量简写案例()
    
    	val list = List(-11,-10,-5,0,5,10)
    
    完整版写法:
    	list.filter((x: Int) => x>0)
    
    省略参数类型写法: 编译器可以推断出x必定是整数,因为它知道过滤的是一个由整数组成的列表。一个表达式的目标使用场景可以影响该表达式的类型。
    				目标类型机制的细节不重要,随着时间的推移,就会慢慢有感觉。什么时候能推断出来,什么时候无法推断。
    	list.filter( (x)=> x>0)
    
    省略圆括号()写法: 只有一个参数x,可以省去圆括号
    	list.filter( x => x>0)
    
    使用占位符_的写法:x代表列表中的元素,每一个元素值在字面量函数中出现一次。可以用_代替x
    	list.filter( _ > 0)
    
    在这里插入图片描述

(2)正确使用占位符

  • 为了函数字面量更加精简,使用下划线作为占位符,用来表示一个或者多个参数,必须满足每个参数只在函数字面量中出现一次。 例如 " _ > 0 " 是个非常短的表达式,表示检查某个值是否大于0 的函数。

  • 有时候当你用下画线为参数占位时,编译器可能并没有足够多的信息来推断缺失的参数类型,比如你只写了:_+_
    在这里插入图片描述
    此时,需要给出参数的类型:
    在这里插入图片描述
    注意, "_ + _"将会展开成一个接收两个参数的函数字面量,这就是为什么只有当每个参数在函数字面量中出现不多不少正好一次的时候才能使用这样的精简写法。多个下画线意味着多个参数,而不是对单个参数的重复使用,第一个下画线代表第一个参数,第二个下画线代表第二个参数,以此类推。

  • 说明
    筛选出列表中大于0的函数字面量简写案例(),有以下四种正确的写法:
    在这里插入图片描述
    但是需要注意的是,有以下两种常见的错误写法,编译器无法正确的推断出参数类型
    在这里插入图片描述
    所以为了避免犯错误,在使用字面简写函数的时候,要么使用省略参数类型的形式:list.filter(x => x >0)那么使用占位符形式:list.filter(_ > 0)。必须注意使用占位符,占位符不能出现在 =>的左边(占位符的定位就是代替只在函数字面量中出现一次的变量。)。

三、带参数的函数

  • 函数可以做一个参数传入另一个函数中,那么做为参数的函数的类型是:function1,即:(参数类型)=> 返回类型
    def vauleAtOneQuarter(f: (Double) =>Double) = f(0.25)
    
    vauleAtOneQuarter 接受一个函数作f为参数, 这个函数满足 参数类型是Double,返回值类型是Double。
    整个函数的返回值是 用传入的函数f,计算f(0.25)
    
    调用vauleAtOneQuarter
    
    vauleAtOneQuarter(ceil _)   得到 1.0
    scala> vauleAtOneQuarter(sqrt _)  得到0.5
    
    在这里插入图片描述
  • vauleAtOneQuarter是个一个接受函数参数的函数,因此它是一个"高阶函数"

四、高阶函数

  • 能够接受函数作为参数的函数,叫做高阶函数。可以应用程序更加健壮。
  • 高阶函数可以返回函数类型
    def mulBy(factor: Double) = (x: Double) => factor * x
    
    mulBy函数可以返回一个 能够乘以任何数 的函数
    
    mulBy(3) 可以返回函数  (x: Double) => 3 * x  函数,即返回计算3的倍数的函数
    
    val mul = mulBy(3)
    mul(20)   得到60  320
    在这里插入图片描述
    所以 mulBy函数接受一个Double类型的参数,返回一个类型为 (Doulbe)=> Double的函数。
    因此它的类型为: (Doule) => ((Doulbe) => Double)
    同时mulBy函数涉及了闭包的知识,详情见一下节。

五、部分应用函数(Partially Applied Functions)

  • 在Scala中,当你调用某个函数,传入任何需要的参数时,实际上是应用函数到这些参数上面。例如

    def sum(a: Int, b: Int, c:Int) = a + b + c
    
    将sum函数应用到参数 123。及平时正常的调用sum函数对123求和。
    sum(1,2,3)
    
  • 部分应用函数是一个表达式,在这个表达式中,并不需要给出函数所需要的全部参数,而是给出部分(可以不给)。

    不给任何一个参数的话,可以在sum之后放一个下划线"_"。这将返回一个函数,可以被存到变量中。
    val a = sum _
    a(1,2,3)
    

    Scala编译器将根据部分应用函数sum _ 实例化一个接受三个整数参数的函数值,并将指向新的函数值的引用复制给变量a。可以传入参数,调用a
    在这里插入图片描述
    在这里插入图片描述

  • 部分应用函数之所以叫作部分应用函数,是因为你并没有把那个函数应用到所有入参。拿 sum _ 来说,你没有应用任何不过,完全可以通过给出一些必填的参数来表达一个部分应用的函数。

    val b = sum(1, _: Int, 3)
    b(2)			得到6。 本质上是 b.apply调用了sum(1,2,3)
    b(5)            得到9。本质上是b.apply调用了sum(1,5,3)
    

    在这里插入图片描述

  • 如果部分应用函数表达式并不给出任何参数且编辑器明确的知道是一个函数。那么下划线也可以省掉。

    比如
    list.foreach(println _) 
    
    可以写成
    list.foreach(println)
    

    在这里插入图片描述

    注意:这种形式只在明确需要函数的地方被允许,本例中的foreach的调用,**编译器知道这里需要的是一个函数。**因为foreach本身要求一个函数作为入参。在那些不需要函数的场景,会编译出错
    在这里插入图片描述

©️2020 CSDN 皮肤主题: 1024 设计师:上身试试 返回首页