Scala使用下划线作为对象占位符

时间:2016-05-08 04:49:09

标签: scala

试图围绕_的各种用途。现在我正在努力解决这个问题:

object Chapter9 extends App {
  FileMatcher.filesEnding(".scala").foreach(println)
}

object FileMatcher {
  private def filesHere = (new java.io.File(".")).listFiles

  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))
}

很明显,我们想要为不同类型的匹配器抽象出循环/过滤/让步的常见工作,将它放在辅助函数中是有意义的。

我被挂在_.endsWith部分。我的理解是,这个下划线(作为方法体中使用的第一个也是唯一一个)将由第一个参数填充,在本例中为query。我试着通过这样做来试验这个理论:

def filesEnding(query: String) = {
  println(_: String)
}

但该程序不会打印任何内容。那么这里的_是什么? Scala如何知道要搜索endsWith方法的对象?

从程序的输出看起来,不知怎的file为这个下划线填写但不知道如何。也许下划线仍然是一个“通配符”,直到它在filesMatching的正文中使用,到那时最近的封闭范围是for和第一个“参数is文件?” / p>

2 个答案:

答案 0 :(得分:2)

查看filesMatching()的签名。请注意,它需要一个类型为String => Boolean的参数。所以它的参数是一个函数,它本身采用String参数并将其转换为Boolean

现在请记住,匿名函数通常看起来像这样:

{ x => /* do something with x */ }

如果x仅使用一次,那么可以缩写为单_。所以,倒退,这个

filesMatching(_.endsWith(query))

可以改写为

filesMatching(x => x.endsWith(query))

所以filesMatching()代码有它的参数,一个带字符串的函数(在匿名函数中我称之为x)。使用字符串matcher调用该函数file.getName以获取Boolean。该布尔值在if子句中进行测试:

if matcher(file.getName)

TL; DR :下划线是file.getName字符串的简写。

答案 1 :(得分:0)

规范的答案是What are all the uses of an underscore in Scala?

-Xprint:parser显示

((x$1: String) => println((x$1: String)))

除了函数体中的冗余类型表达式之外,它是无趣的。

似乎没有生成任何额外的代码。该参数已经是一个字符串。

我不认为你的例子编译?或者我不知道你在问什么。

当没有按照您的意愿推断出匿名函数的类型时,显式类型可以帮助调试。

编辑:我试了一下:

object Chapter9 extends App {
  FileMatcher.filesEnding(".scala").foreach(println)
}

object FileMatcher {
  private def filesHere = (new java.io.File(".")).listFiles

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

使用下划线作为匿名函数的表达式需要其预期类型来告诉它下划线是什么类型,除非您明确注释。但这并不常见。

而不是(_: Int) * 2(i: Int) => i * 2,但这是一个风格问题。