Scala 之 占位符(即下划线)语法二

需求

已知某路径下有若干文件,返回文件名满足如下条件的文件列表:

  • 文件名以xxx结尾的
  • 文件名中包含xxx的
  • 文件名匹配某正则表达式的
举例

某路径下有如下文件:

  • Some.java
  • Some2.scala
  • Some678Other.scala
  • SomeJdbcOther1.scala
  • SomeJdbcOther2.java

输出满足“文件名以scala结尾”、“文件名包含Jdbc”、“文件名中有3位数字”的文件列表(此处省略了绝对路径):

  • Some2.scala, Some678Other.scala, SomeJdbcOther1.scala
  • SomeJdbcOther1.scala, SomeJdbcOther2.java
  • Some678Other.scala
第一个版本
//Files1.scala
object Files1 {
  private def filesHere = (new java.io.File("D:/Temp/20200420/")).listFiles

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

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

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

  def main(args: Array[String]) {
    println(filesEnding("scala").toList)
    println("===")
    println(filesContaining("Jdbc").toList)
    println("===")
    println(filesRegex(".*\\d{3}.*").toList)
  }
}

需求要什么就满足什么,这是人的一般思维方式,第一个版本很好理解,注意“yield file”这种用法,即把for循环的返回值收集到一个数组中,最终返回Array[File]。

第二个版本(重构)

如果需要列出以xxx开头的文件名,可能需要新增一个方法,如def filesStartWith(query: String)。你会发现这个新方法和filesEnding、filesContaining、filesRegex比较类似,代码存在重复的“坏味道”。把文件过滤条件抽取出来,不失为一个好注意。

//Files2.scala
object Files2 {
  private def filesHere = (new java.io.File("D:/Temp/20200420/")).listFiles

  /**
    * matcher函数的第一个参数表示:文件名称
    * matcher函数的第二个参数表示:文件后缀、文件名称特征字符串或者正则表达式
    */
  def filesMatching(query: String, matcher: (String, String) => Boolean) = {
    for (file <- filesHere; if matcher(file.getName, query))
      yield file
  }

  def filesEnding(query: String) =
    filesMatching(query, (fileName: String, query: String) => fileName.endsWith(query))

  def filesContaining(query: String) =
    filesMatching(query, (fileName: String, query: String) => fileName.contains(query))

  def filesRegex(query: String) =
    filesMatching(query, (fileName: String, query: String) => fileName.matches(query))

  def main(args: Array[String]) {
    println(filesEnding("scala").toList)
    println("===")
    println(filesContaining("Jdbc").toList)
    println("===")
    println(filesRegex(".*\\d{3}.*").toList)
  }
}
第三个版本(重构)

Scala有类型推断特性,(fileName: String, query: String)可简写为(fileName, query)

//Files3.scala
object Files3 {
  private def filesHere = (new java.io.File("D:/Temp/20200420/")).listFiles

  /**
    * matcher函数的第一个参数表示:文件名称
    * matcher函数的第二个参数表示:文件后缀、文件名称特征字符串或者正则表达式
    */
  def filesMatching(query: String, matcher: (String, String) => Boolean) = {
    for (file <- filesHere; if matcher(file.getName, query))
      yield file
  }

  def filesEnding(query: String) =
    filesMatching(query, (fileName, query) => fileName.endsWith(query))

  def filesContaining(query: String) =
    filesMatching(query, (fileName, query) => fileName.contains(query))

  def filesRegex(query: String) =
    filesMatching(query, (fileName, query) => fileName.matches(query))

  def main(args: Array[String]) {
    println(filesEnding("scala").toList)
    println("===")
    println(filesContaining("Jdbc").toList)
    println("===")
    println(filesRegex(".*\\d{3}.*").toList)
  }
}
第四个版本(重构)

对于(fileName, query) => fileName.endsWith(query),由于参数fileName和query在函数体内分别只用到了一次,可以用占位符语法来写:_endsWith(_)。

//Files4.scala
object Files4 {
  private def filesHere = (new java.io.File("D:/Temp/20200420/")).listFiles

  /**
    * matcher函数的第一个参数表示:文件名称
    * matcher函数的第二个参数表示:文件后缀、文件名称特征字符串或者正则表达式
    */
  def filesMatching(query: String, matcher: (String, String) => Boolean) = {
    for (file <- filesHere; if matcher(file.getName, query))
      yield file
  }

  def filesEnding(query: String) =
    filesMatching(query, _.endsWith(_))

  def filesContaining(query: String) =
    filesMatching(query, _.contains(_))

  def filesRegex(query: String) =
    filesMatching(query, _.matches(_))

  def main(args: Array[String]) {
    println(filesEnding("scala").toList)
    println("===")
    println(filesContaining("Jdbc").toList)
    println("===")
    println(filesRegex(".*\\d{3}.*").toList)
  }
}
第五个版本(重构)

matcher函数的第二个参数,其实来自filesEnding(query: String)里的query,并没有被改变,因而filesMatching(query, _.endsWith(_))里query参数显得也重复了,利用Scala的闭包特性可进一步重构。

//Files5.scala
object Files5 {
  private def filesHere = (new java.io.File("D:/Temp/20200420/")).listFiles

  /**
    * matcher函数的第一个参数表示:文件名称
    * matcher函数的第二个参数表示:文件后缀、文件名称特征字符串或者正则表达式
    */
  private 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))

  def main(args: Array[String]) {
    println(filesEnding("scala").toList)
    println("===")
    println(filesContaining("Jdbc").toList)
    println("===")
    println(filesRegex(".*\\d{3}.*").toList)
  }
参考

1.《Scala编程(第3版)》9.1 减少代码重复

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值