Scala 函数式编程高级

函数式编程高级

  • 偏函数

  • 值函数

  • 作为参数的函数

  • 匿名函数

  • 参数(类型)推断 

  • 高阶函数

  • 闭包

  • 柯里化

  • 抽象控制

 

1.偏函数

  • 在对符合某个条件,而不是所有情况进行逻辑操作时,使用偏函数是一个不错的选择。

  • 将包在大括号内的一组case语句封装为函数,我们称之为偏函数,它只对会作用于指定类型的参数或指定范围值的参数实施计算,超出范围的值会忽略(未必会忽略,这取决于你打算怎样处理)

  • 偏函数在Scala中是一个特质PartialFunction

1.1 提出一个需求,引起思考:

    给你一个集合 val list = List(1, 2, 3, 4, "abc") ,请完成如下要求:

    1) 将集合 list 中的所有数字+1,并返回一个新的集合

    2) 要求忽略掉 非数字 的元素,即返回的 新的集合 形式为 (2, 3, 4, 5)

1.2 解决方式1 

    filter + map 返回新的集合:虽然可以解决问题,但是麻烦。、

object Scala01_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))
  }
  def f1(n: Any): Boolean = {
    n.isInstanceOf[Int]
  }
  def f2(n: Int): Int = {
    n + 1
  }
  def f3(n: Any): Int = {
    n.asInstanceOf[Int]
  }
}

1.3 解决方式2 

    模式匹配:虽然使用模式匹配比较简单,但是不够完美。

object Scala01_PartialFunDemo02 {
  def main(args: Array[String]): Unit = {
    val list = List(1, 2, 3, 4, "hello")
    // 思路2:模式匹配
    // 虽然使用模式匹配比较简单,但是不够完美
    val list2 = list.map(addOne2)
    println("list2=" + list2)
  }
  def addOne2( i : Any ): Any = {
    i match {
      case x:Int => x + 1
      case _ =>
    }
  }
}

1.4 使用偏函数

object Scala01_PartialFunDemo03 {
  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)
  }
}

1.5 偏函数的简写形式

object Scala01_PartialFunDemo04 {
  def main(args: Array[String]): Unit = {

    //第一种简写形式
    def partialFun2: PartialFunction[Any,Int] = {
      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) 
    
  }
}

1.6 偏函数的小结

  • 使用构建特质的实现类(使用的方式是 PartialFunction 的匿名子类)。

  • PartialFunction 是个特质(看源码)。

  • 构建偏函数时,参数形式 [Any, Int]是泛型,第一个表示参数类型,第二个表示返回参数。

  • 当使用偏函数时,会遍历集合的所有元素,编译器执行流程时先执行 isDefinedAt()如果为 true ,就会执行 apply, 构建一个新的 Int 对象返回。

  • 执行 isDefinedAt() 为 false 就过滤掉这个元素,即不构建新的 Int 对象。

  • map函数不支持偏函数,因为map底层的机制就是所有循环遍历,无法过滤处理原来集合的元素。

  • collect函数支持偏函数。

 

2.值函数(默认参数值函数)

    Scala允许您指定函数参数的默认值。 这样一个参数可以从函数调用中选择性地省略,在这种情况下,相应的参数值将使用默认值。如果指定其中一个参数,则使用该参数将传递第一个参数,第二个参数将从默认值中获取。

    尝试以下示例,它是为函数指定默认参数的示例。

object Scala02_ValueDemo {
    def main(args: Array[String]) {
      println( "Returned Value : " + addInt() );
    }
    def addInt( a:Int = 5, b:Int = 7 ) : Int = {
      var sum:Int = 0
      sum = a + b
      return sum
    }
}

3.作为参数的函数

    函数作为一个变量传入到了另一个函数中,那么该作为参数的函数的类型是:function1,即:(参数类型) => 返回类型

object Scala03_FunParameter {
  def main(args: Array[String]): Unit = {
    def plus(x: Int) = 3 + x
    val result1 = Array(1, 2, 3, 4).map(plus(_))
    println(result1.mkString(",")) //(4,5,6,7)
    println("puls的函数类型function1" + (plus _))
  }
}

// 说明:
// 1) map(plus(_)) 中的 plus(_) 就是将 plus 这个函数当做一个参数传给了 map,_这里代表从集合中 遍历出来的一个元素。
// 2) plus(_) 这里也可以写成 plus 表示对 Array(1,2,3,4) 遍历,将每次遍历的元素传给 plus 的 x 
// 3) 进行 3 + x 运算后,返回新的 Int ,并加入到新的集合 result1 中
// 4) def map[B, That](f: A => B) 的声明中的 f: A => B 一个函数

4.匿名函数 

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

object Scala04_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)) 
  }
}

5.参数(类型)推断 

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

/**
 * 参数(类型)推断
 * 1) 参数类型是可以推断时,可以省略参数类型
 * 2) 当传入的函数,只有单个参数时,可以省去括号 
 * 3) 如果变量只在=>右边只出现一次,可以用_来代替
 */
object Scala05_ParameterInfer {
  def main(args: Array[String]): Unit = {
    val list = List(1, 2, 3, 4)
    println(list.map((x:Int)=>x + 1)) //(2,3,4,5)
    println(list.map((x)=>x + 1)) //(2,3,4,5)
    println(list.map(x=>x + 1)) //(2,3,4,5)
    println(list.map( _ + 1)) //(2,3,4,5)
    println(list.reduce(f1)) // 10
    println(list.reduce((n1:Int ,n2:Int) => n1 + n2)) //10
    println(list.reduce((n1 ,n2) => n1 + n2)) //10
    println(list.reduce( _ + _)) //10
    val res = list.reduce(_+_)
  }
  def f1(n1:Int ,n2:Int): Int = {
    n1 + n2
  }
}

6.高阶函数 

6.1 基本介绍

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

object Scala04_HigherOrderFunction {
  def main(args: Array[String]): Unit = {
    def test(f: Double => Double, f2: Double =>Int ,  n1: Double) = {
      f(f2(n1)) // f(0)
    }
    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)
  }
}

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

object Scala04_HigherOrderFunction2 {
  def main(args: Array[String]): Unit = {

    //说明
    //1. minusxy是高阶函数,因为它返回匿名函数
    //2. 返回的匿名函数 (y: Int) => x - y
    //3. 返回的匿名函数可以使用变量接收

    def minusxy(x: Int) = {
      (y: Int) => x - y
    }
    val f1 = minusxy(3)
    println("f1的类型=" + f1)
    println(f1(1)) //  2
    println(f1(9)) //  -6
    println(minusxy(4)(9)) // -5
    
  }
}

7.闭包(closure) 

    闭包就是一个函数和与其相关的引用环境组合的一个整体(实体),参考class中当方法跟属性 组成当闭包。

object Scala07_ClosureDemo01 {
  def main(args: Array[String]): Unit = {
    /*
    请编写一个程序,具体要求如下:
    1.编写一个函数 makeSuffix(suffix: String)  可以接收一个文件后缀名(比如.jpg),并返回一个闭包
    2.调用闭包,可以传入一个文件名,如果该文件名没有指定的后缀(比如.jpg) ,则返回 文件名.jpg , 如果已经有.jpg后缀,则返回原文件名。
       比如 文件名 是 dog =>dog.jpg
      比如  文件名 是 cat.jpg => cat.jpg
    3.要求使用闭包的方式完成
      提示:String.endsWith(xx)
     */


    val f = makeSuffix(".jpg")
    println(f("dog.jpg")) // dog.jpg
    println(f("cat")) // cat.jpg
  }
  
  def makeSuffix(suffix: String) = {
    //返回一个匿名函数,回使用到suffix
    (filename:String) => {
      if (filename.endsWith(suffix)) {
        filename
      } else {
        filename + suffix
      }
    }
  }
  
}

8.颗粒化

    函数编程中,接受多个参数的函数都可以转化为接受单个参数的函数,这个转化过程就叫柯里化。

    柯里化就是证明了函数只需要一个参数而已。其实我们刚才的学习过程中,已经涉及到了柯里化操作。

    不用设立柯里化存在的意义这样的命题。柯里化就是以函数为主体这种思想发展的必然产生的结果。(即:柯里化是面向函数思想的必然产生结果)。

object Scala07_CurryDemo01 {
  def main(args: Array[String]): Unit = {
    def mulCurry2(x: Int)(y:Int) = x * y
    println(mulCurry2(10)(8))
  }
}

9.控制抽象

    控制抽象是这样的函数,满足如下条件。

1) 参数是函数

2) 函数参数没有输入值也没有返回值

    控制抽象的应用案例(使用控制抽象实现了 while 语法)

/**
 * 控制抽象的应用案例(使用控制抽象实现了 while 语法)
 */
object Scala09_AbstractControl {
  def main(args: Array[String]): Unit = {
    //myRunInThread 就是一个抽象控制
    //是没有输入,也没有输出的函数 f1: () => Unit
    def myRunInThread(f1: () => Unit) = {
      new Thread {
        override def run(): Unit = {
          f1()
        }
      }.start()
    }
    myRunInThread {
      () =>
        println("干活咯!5秒完成...")
        Thread.sleep(5000)
        println("干完咯!")


    }


    //简写形式
    def myRunInThread2(f1:  => Unit) = {
      new Thread {
        override def run(): Unit = {
          f1
        }
      }.start()
    }
    //对于没有输入,也没有返回值函数,可以简写成如下形式
    myRunInThread2 {
      println("干活咯!5秒完成...~~~")
      Thread.sleep(5000)
      println("干完咯!~~~")
    }
    
  }
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序员学习圈

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值