Scala之函数式编程高级

偏函数(partial function)
提出一个需求,引起思考
给你一个集合 val list = List(1, 2, 3, 4, "abc") ,请完成如下要求:
1) 将集合 list 中的所有数字+1,并返回一个新的集合
2) 要求忽略掉 非数字 的元素,即返回的 新的集合 形式为 (2, 3, 4, 5)
解决方式-filter + map 返回新的集合, 引出偏函数
解决方式-模式匹配

/*
给你一个集合 val list = List(1, 2, 3, 4, "abc") ,请完成如下要求:
将集合 list 中的所有数字+1,并返回一个新的集合
要求忽略掉 非数字 的元素,即返回的 新的集合 形式为 (2, 3, 4, 5)
*/
object PartialFunDemo01 {
  def main(args: Array[String]): Unit = {
    // 思路 1 filter + map  方式解决
    // 虽然可以解决问题,但是麻烦.
    val list = List(1, 2, 3, 4, "hello")
    //  先过滤,再 map
    println(list.filter(f1).map(f3).map(f2))
    // 思路 2- 模式匹配
    // 小结:虽然使用模式匹配比较简单,但是不够完美
    val list2 = list.map(addOne2)
    println("list2=" + list2)
  }
  //模式匹配
  def addOne2( i :Any ):Any = {
    i match {
      case x:Int => x + 1
      case _ =>
    }
  }
  def f1(n: Any): Boolean = {
    n.isInstanceOf[Int]
  }
  def f2(n: Int): Int = {
    n + 1
  }
  //将 Any->Int [map]
  def f3(n: Any): Int = {
    n.asInstanceOf[Int]
  }
}

偏函数使用方式 

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

7) collect 函数支持偏函数

object PartialFunDemo02 {
  def main(args: Array[String]): Unit = {
    //使用偏函数解决
    val list = List(1, 2, 3, 4, "hello")
    //定义一个偏函数
    //1. PartialFunction[Any,Int] 表示偏函数接收的参数类型是 Any,返回类型是 Int
    //2. isDefinedAt(x:Any) 如果返回 true ,就会去调用 apply 构建对象实例,如果是 false,过滤
    //3. apply 构造器 ,对传入的值 + 1,并返回(新的集合)
    val partialFun = new PartialFunction[Any,Int] {
      override def isDefinedAt(x:Any) = {
        println("x=" + x)
          x.isInstanceOf[Int]
      }
      override def apply(v1:Any) = {
        println("v1=" + v1)
        v1.asInstanceOf[Int] + 1
      }
    }
    //使用偏函数
    //说明:如果是使用偏函数,则不能使用 map,应该使用 collect
    //说明一下偏函数的执行流程
    //1. 遍历 list 所有元素
    //2. 然后调用 val element = if(partialFun-isDefinedAt(list 单个元素)) {partialFun-apply(list 单个元素) }
  //3. 每得到一个 element,放入到新的集合,最后返回
  val list2 = list.collect(partialFun)
  println("list2" + list2)
}
}

偏函数简写的两种方式 

object PartialFun03 {
  def main(args: Array[String]): Unit = {
    //可以将前面的案例的偏函数简写
    def partialFun2: PartialFunction[Any,Int] = {
      //简写成 case 语句
      case i:Int => i + 1
      case j:Double => (j * 2).toInt
    }
    val list = List(1, 2, 3, 4, 1.2, 2.4, 1.9f, "hello")
    val list2 = list.collect(partialFun2)
    println("list2=" + list2)
    //第二种简写形式
    val list3 = list.collect{
      case i:Int => i + 1
      case j:Double => (j * 2).toInt
      case k:Float => (k * 3).toInt
    }
    println("list3=" + list3) // (2,3,4,5)
  }
}

匿名函数

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

object AnonymouseFunction {
  def main(args: Array[String]): Unit = {
    //对匿名函数的说明
    //1. 不需要写 def 函数名
    //2. 不需要写返回类型,使用类型推导
    //3. = 变成 =>
    //4. 如果有多行,则使用{} 包括
    val triple = (x: Double) => {
      println("x=" + x)
      3 * x
    }
    println("triple" + triple(3)) // 9.0
    println(triple)
  }
}

高阶函数

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

object HigherOrderFunction {
  def main(args: Array[String]): Unit = {
    def test(f: Double => Double, f2: Double =>Int , n1: Double) = {
      f(f2(n1)) // f(0)
    }
    //sum 是接收一个 Double,返回一个 Double
    def sum(d: Double): Double = {
      d + d
    }
    def mod(d:Double): Int = {
      d.toInt % 2
    }
    val res = test(sum, mod, 5.0) //
    println("res=" + res) // 2.0
  }
}

高阶函数可以返回函数类型

object HigherOrderFunction2 {
  def main(args: Array[String]): Unit = {
    //说明
    //1. minusxy 是高阶函数,因为它返回匿名函数
    //2. 返回的匿名函数 (y: Int) => x - y
    //3. 返回的匿名函数可以使用变量接收
    def minusxy(x: Int) = {
        (y: Int) => x - y //匿名函数
    }
    //分步执行
    //f1 就是 (y: Int) => 3 - y
    val f1 = minusxy(3)
    println("f1 的类型=" + f1)
    println(f1(1)) // 2
    println(f1(9)) // -6
    //也可以一步到位的调用
    println(minusxy(4)(9)) // -5
  }
}

参数(类型)推断

参数推断省去类型信息(在某些情况下[ 需要有应用场景],参数类型是可以推断出来的,如
list=(1,2,3) list.map() map 中函数参数类型是可以推断的),同时也可以进行相应的简写。

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

object ParameterInfer {
  def main(args: Array[String]): Unit = {
    val list = List(1, 2, 3, 4)
    println(list.map((x:Int)=>x + 1)) //使用匿名函数化简
    println(list.map((x)=>x + 1)) //因为参数类型可以推导为int,所以int可以省略
    println(list.map(x=>x + 1)) //参数只有一个,小括号也可以去掉
    println(list.map( _ + 1)) //=>这个符号右边x只出现了一次,所以可以把x=>x代替为_


    println(list.reduce(f1)) // 10
    println(list.reduce((n1:Int ,n2:Int) => n1 + n2)) //使用匿名函数化简
    println(list.reduce((n1 ,n2) => n1 + n2)) //因为参数类型可以推导为int,所以int可以省略
    println(list.reduce( _ + _)) //=>这个符号右边n1,n2只出现了一次,所以可以代替为_
    val res = list.reduce(_+_)
  }
  def f1(n1:Int ,n2:Int): Int = {
    n1 + n2
  }
}

闭包(closure)

基本介绍:闭包就是 一个函数和与其相关的引用环境组合的一个 整体(实体)。

1) 第 1 点
(y: Int) => x – y
返回的是一个匿名函数 ,因为该函数引用到到函数外的 x,那么 该函数和 x 整体形成一个闭包
如:这里 val f = minusxy(20) 的 f 函数就是闭包
2) 你可以这样理解,返回函数是一个对象,而 x 就是该对象的一个字段,他们共同形成一个闭包
3) 当多次调用 f 时(可以理解多次调用闭包),发现使用的是同一个 x, 所以 x 不变。
4) 在使用闭包时,主要搞清楚返回函数引用了函数外的哪些变量,因为他们会组合成一个整体(实
体),形成一个闭包

闭包的好处,就是传入一个值,可以反复使用

object ParameterInfer {
  def main(args: Array[String]): Unit = {
    //1.用等价理解方式改写 2.对象属性理解
    def minusxy(x: Int) = (y: Int) => x - y
    //f 函数就是闭包.   (y: Int) => x - y 和传入的20 合起来就是闭包,一个函数和外部引用的变量合一块.
    val f = minusxy(20)
    println("f(1)=" + f(1)) // 19
    println("f(2)=" + f(2)) // 18

  }
}

函数柯里化(curry)

基本介绍
1) 函数编程中,接受多个参数的函数都可以转化为接受单个参数的函数,这个转化过程就叫柯里

2) 柯里化就是证明了函数只需要一个参数而已。其实我们刚才的学习过程中,已经涉及到了柯里
化操作。
3) 不用设立柯里化存在的意义这样的命题。柯里化就是以函数为主体这种思想发展的必然产生的
结果。(即:柯里化是面向函数思想的必然产生结果)

编写一个函数,接收两个整数,可以返回两个数的乘积,要求:

object ParameterInfer {
  def main(args: Array[String]): Unit = {
    //说明
    //使用常规的方式完成
    def mul(x: Int, y: Int) = x * y
    println(mul(10, 10))
    //使用闭包的方式完成
    def mulCurry(x: Int) = (y: Int) => x * y
    println(mulCurry(10)(9))
    //使用函数柯里化完成
    def mulCurry2(x: Int)(y:Int) = x * y
    println(mulCurry2(10)(8))
  }
}

 函数柯里化最佳实践
比较两个字符串在忽略大小写的情况下是否相等,注意,这里是两个任务:
1) 全部转大写(或小写)
2) 比较是否相等
针对这两个操作,我们用一个函数去处理的思想,其实也变成了两个函数处理的思想(柯里化)
使用函数柯里化的思想来任务

柯里化作用主要是把实现功能的步骤拆分成不同的方法,通过动态添加方法来实现

object CurryDemo02 {
  def main(args: Array[String]): Unit = {
    //这是一个函数,可以接收两个字符串,比较是否相等
    def eq(s1: String, s2: String): Boolean = {
      s1.equals(s2)
    }
    //类 隐式类 ( 可以参考前面讲解内容)
    implicit class TestEq(s: String) {
      // 体现了将比较字符串的事情,分解成两个任务完成
      //1. checkEq  完转换大小写
      //2. f  函数完成比较任务
      def checkEq(ss: String)(f: (String, String) => Boolean): Boolean = {
        f(s.toLowerCase, ss.toLowerCase)
      }
    }
    val str1 = "hello"
    println(str1.checkEq("HeLLO")(eq))
    
    //匿名函数
    println(str1.checkEq("HeLLO")((s1: String, s2: String) => s1.equals(s2)))
    //在看一个简写形式
    println(str1.checkEq("HeLLO")(_.equals(_)))
  }
}

控制抽象

如何实现将一段代码(从形式上看),作为参数传递给高阶函数,在高阶函数内部执行这段代码. 其
使用的形式如 breakable{} 。

var n = 10
breakable {
    while (n <= 20) {
    n += 1
    if (n == 18) {
        break()
    }
    }
}

控制抽象是这样的函数,满足如下条件
1) 参数是函数
2) 函数参数没有输入值也没有返回值

object AbstractControl {
  def main(args: Array[String]): Unit = {
    //myRunInThread 就是一个抽象控制
    //是没有输入, 也没有输出的函数 f1: () => Unit
    def myRunInThread(f1: () => Unit) = {
      new Thread {
        override def run(): Unit = {
          f1() //只写了 f1
        }
      }.start()
    }
    myRunInThread {
      () =>
        println("干活咯!5 秒完成...")
        Thread.sleep(5000)
        println("干完咯!")
    }
    //简写形式  可以把小括号去掉
    def myRunInThread2(f1: => Unit) = {
      new Thread {
        override def run(): Unit = {
          f1 //只写了 f1
        }
      }.start()
    }
    //对于没有输入,也没有返回值函数,可以简写成如下形式
    myRunInThread2 {
      println("干活咯!5 秒完成...~~~")
      Thread.sleep(5000)
      println("干完咯!~~~")
    }
  }
}

进阶用法:实现类似 while 的 until 函数

object AbstractControl2 {
  def main(args: Array[String]): Unit = {
    var x = 10
    //说明
    //1 函数名为 until , 实现了类似 while 循环的效果
    //2. condition: => Boolean 是后一个没有输入值,返回 Boolean 类型函数
    //3. block: => Unit 没有输入值,也没有返回值的韩
    def mywhile(condition: => Boolean)(block: => Unit): Unit = {
      //类似 while 循环,递归
      if(condition) {
        block // x= 9 ,x = 8 x =7 ....
        mywhile(condition)(block)
      }
    }
    mywhile(x > 0) {
      x -= 1
      println("x=" + x )
    }
  }
}

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值