Scala 减低代码重复

2018-09-28 18:25 更新

减低代码重复

在前面的文章中,我们说过 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,因此我们可以看到闭包也可以用来简化代码。

以上内容是否对您有帮助:
在线笔记
App下载
App下载

扫描二维码

下载编程狮App

公众号
微信公众号

编程狮公众号