目录

Scala 减低代码重复

2018年03月09日 09:05 | 132次浏览 | 分类: 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,因此我们可以看到闭包也可以用来简化代码。



感觉本站内容不错,读后有收获?小额赞助,鼓励网站分享出更好的教程