×
Scala 教程Scala 简介Scala 安装及环境配置Scala 起步Scala 进一步Scala 基础语法Scala 数据类型Scala 变量Scala 访问修饰符Scala 运算符Scala IF...ELSE 语句Scala 循环Scala 函数Scala 闭包Scala 字符串Scala 数组Scala Collection(集合)Scala Match 表达式Scala Iterator(迭代器)没有“break”和“continue”Scala 类和对象Scala Trait(特征)Scala Trait 的基本概念选择瘦接口还是胖接口设计?Scala Trait 示例–RectangularScala Ordered TraitTrait 用来实现可叠加修改操作Scala 模式匹配Scala 正则表达式Scala 异常处理Scala 提取器(Extractor)Scala 文件 I/OScala 减低代码重复Scala 柯里化函数Scala 创建新的控制结构Scala 传名参数Scala 的类层次关系Scala 基本数据类型的实现方法Scala 底层类型

Scala 减低代码重复


减低代码重复

在前面的文章中,我们说过 Scala 没有内置很多控制结构,这是因为 Scala 赋予了程序员自己扩展控制结构的能力。Scala 支持函数值(值的类型为函数,而非函数的返回值),为避免混淆,我们使用函数类型值来指代类型为函数的值。

所有的函数可以分成两个部分:一是共有部分,这部分在该函数的调用都是相同的,另外一部分为分公共部分,这部分在每次调用该函数上是可以不同的。公共部分为函数的定义体,非公共部分为函数的参数。但你使用函数类型值做为另外一个函数的参数时,函数的非公共部分本身也是一个算法(函数),调用该函数时,每次你都可以传入不同函数类型值作为参数,这个函数称为高阶函数–函数的参数也可以是另外一个函数。

使用高级函数可以帮助你简化代码,它支持创建一个新的程序控制结构来减低代码重复。比如,你打算写一个文件浏览器,你需要写一个 API 支持搜索给定条件的文件。首先,你添加一个方法,该方法可以通过查询包含给定字符串的文件,比如你可以查所有“.scala”结尾的文件。你可以定义如下的 API:

object FileMatcher {
  private def filesHere = (new java.io.File(".")).listFiles
  def filesEnding(query : String) =
    for (file <-filesHere; if file.getName.endsWith(query))
      yield file
}

filesEnding 方法从本地目录获取所有文件(方法 filesHere),然后使用过滤条件(文件以给定字符串结尾)输出给定条件的文件。

到目前为止,这代码实现非常好也没有什么重复的代码。后来,你有需要使用新的过滤条件,文件名包含指定字符串,而不仅仅以某个字符串结尾的文件列表。你有实现了下面的 API。

def filesContaining( query:String ) =
    for (file <-filesHere; if file.getName.contains(query))
      yield file

filesContaining 和 filesEnding 的实现非常类似,不同点在于一个使用 endsWith,另一个使用 contains 函数调用。有过了一段时间,你有想支持使用正则表达式来查询文件,你有实现了下面的对象方法:

def filesRegex( query:String) =
   for (file <-filesHere; if file.getName.matches(query))
      yield file

这三个函数的算法非常类似,所不同的是过滤条件稍有不同,在 Scala 中我们可以定义一个高阶函数,将这三个不同过滤条件抽象称一个函数作为参数传给搜索算法,我们可以定义这个高阶函数如下:

def filesMatching( query:String, 
    matcher: (String,String) => Boolean) = {
    for(file <- filesHere; if matcher(file.getName,query))
      yield file
   }

这个函数的第二个参数 matcher 的类型也为函数(如果你熟悉 C#,类似于 delegate),该函数的类型为 (String,String ) =>Boolean,可以匹配任意使用两个 String 类型参数,返回值类型为 Boolean 的函数。使用这个辅助函数,我们可以重新定义 filesEnding,filesContaining 和 filesRegex。

def filesEnding(query:String) =
   filesMatching(query,_.endsWith(_))
def filesContaining(query:String)=
   filesMatching(query,_.contains(_))
def filesRegex(query:String) =
   filesMatching(query,_.matches(_))

这个新的实现和之前的实现已经简化了不少,实际上代码还可以简化,我们注意到参数 query 在 filesMatching 的作用只是把它传递给 matcher 参数,这种参数传递实际也是无需的,简化后代码如下:

object FileMatcher {
  private def filesHere = (new java.io.File(".")).listFiles
  def filesMatching(
    matcher: (String) => Boolean) = {
    for(file <- filesHere; if matcher(file.getName))
      yield file
   }
  def filesEnding(query:String) =
   filesMatching(_.endsWith(query))
def filesContaining(query:String)= 
   filesMatching(_.contains(query))
def filesRegex(query:String) = 
   filesMatching(_.matches(query))
}

函数类型参数 .endsWith(query),.contains(query)和_.matches(query)为函数闭包,因为它们绑定了一个自由变量 query,因此我们可以看到闭包也可以用来简化代码。


分类导航

关注微信下载离线手册

bootwiki移动版 bootwiki
(群号:472910771)