zoukankan      html  css  js  c++  java
  • scala之旅-核心语言特性【高阶函数】(十)

    高阶函数是将其他函数作为形参,或者以函数作为返回结果。因为在Scala中,函数是一等公民。这个术语可能听起来有点乱,但实际上我们把 以函数作为形参或以函数作为返回结果的函数和方法统称为高阶函数。

    在一个纯粹的面向对象编程中,隐藏可能会暴露对象内部状态的参数是一个很好的解决方案,泄漏内部状态可能会破坏对象的内部的不变性。从而导致违反封装性原则。

    一个最常用的案例就是高阶函数 map,在scala中经常用于处理集合的。

    val salaries = Seq(20000, 70000, 40000)
    val doubleSalary = (x: Int) => x * 2
    val newSalaries = salaries.map(doubleSalary) // List(40000, 140000, 80000)

    doubleSalary 是一个函数,这个函数一个 Int 类型的x 作为形参,然后返回 x*2 。一般情况下,=> 左边的元组是一个参数列表,右边则是一个返回的表达式。在第3行,函数 doubleSalary 将作用于 salaries 中的每一个元素。

    为了简化这段代码,我们可以使用一个匿名函数作为实参直接传给map。

    val salaries = Seq(20000, 70000, 40000)
    val newSalaries = salaries.map(x => x * 2) // List(40000, 140000, 80000)

    注意,x 没有在上面的例子中直接声明为 Int 类型。这是因为可以根据 map的期望的函数类型直接推断出 x的类型(参见 currying,编写如上这段代码更惯用的方式)。

    val salaries = Seq(20000, 70000, 40000)
    val newSalaries = salaries.map(_ * 2)

    因为Scala编译器已经知道了参数的类型了(一个单独的Int)。你只需要在右边提供一个函数。值得注意的是,你可以用一个 _ 来代替形参名 (这个就等价于之前例子中的 x)。

    将方法强转为函数

    当然也可以将方法作为实参传递给高阶函数。因为Scala编译器会将方法强转为函数.

    case class WeeklyWeatherForecast(temperatures: Seq[Double]) {
    
      private def convertCtoF(temp: Double) = temp * 1.8 + 32
    
      def forecastInFahrenheit: Seq[Double] = temperatures.map(convertCtoF) // <-- passing the method convertCtoF
    }

    这里的 convertCtoF 可以被用来传递给高阶函数map. 这是因为编译器将方法 convertCtoF 强转为函数 x => convertCtoF(x) (注意,x 是一个生成的名字,并且保证其在作用域内是唯一的)

    接收函数的函数

    使用高阶函数的一个原因是为了减少冗余的代码。假设,你想实现一些方法,这些方法根据一些特征来实现提高某人的薪资。在不使用高阶函数的时候,代码可能看起来像下面这个

    object SalaryRaiser {
    
      def smallPromotion(salaries: List[Double]): List[Double] =
        salaries.map(salary => salary * 1.1)
    
      def greatPromotion(salaries: List[Double]): List[Double] =
        salaries.map(salary => salary * math.log(salary))
    
      def hugePromotion(salaries: List[Double]): List[Double] =
        salaries.map(salary => salary * salary)
    }

    注意,每一个方法只随乘数变化而变化。为了简化,你可以将重复的代码抽到高阶函数里面去,如下:

    object SalaryRaiser {
    
      private def promotion(salaries: List[Double], promotionFunction: Double => Double): List[Double] =
        salaries.map(promotionFunction)
    
      def smallPromotion(salaries: List[Double]): List[Double] =
        promotion(salaries, salary => salary * 1.1)
    
      def greatPromotion(salaries: List[Double]): List[Double] =
        promotion(salaries, salary => salary * math.log(salary))
    
      def hugePromotion(salaries: List[Double]): List[Double] =
        promotion(salaries, salary => salary * salary)
    }

    这个新方法 promition 将 salaries 和一个 Double => Double 函数作为形参 (函数以Double作为参数,Double作为返回值) 并返回乘积。

    方法和函数通常用于描述行为和数据的变化。因此用函数去构建其他函数有助于搭建通用的框架。这些通用的操作延迟了整个行为的锁定,从而为客户端提供提供了可控的或者将来可自定义的部分提供了可能。

    函数返回函数

    在某些情况下你可以生成一些函数。如下的例子就是返回一个函数

    def urlBuilder(ssl: Boolean, domainName: String): (String, String) => String = {
      val schema = if (ssl) "https://" else "http://"
      (endpoint: String, query: String) => s"$schema$domainName/$endpoint?$query"
    }
    
    val domainName = "www.example.com"
    def getURL = urlBuilder(ssl=true, domainName)
    val endpoint = "users"
    val query = "id=1"
    val url = getURL(endpoint, query) // "https://www.example.com/users?id=1": String

    注意 urlBuilder返回的类型是 (String,String) => String 。这意味着返回的匿名函数需要传递两个String参数,然后返回一个String值。在这种情况下,返回的匿名函数为(endpoint: String, query: String) => s"https://www.example.com/$endpoint?$query"

  • 相关阅读:
    VC字符串输出对齐问题(转)
    木马免杀全攻略(转)
    Windows Vista自动重启问题解决方法(转)
    图说VSS 6.0构架版本控制系统解决方案(转)
    几个有用的链接
    X64 Windows 2003 及XP 语言包官方下载
    .NET 3.5的版本问题(转)
    设计模式读书笔记工厂方法模式
    设计模式读书笔记装饰者模式
    设计模式读书笔记简单工厂模式
  • 原文地址:https://www.cnblogs.com/zhouwenyang/p/13883454.html
Copyright © 2011-2022 走看看