理解Scala:将函数作为参数传递

6

我开始学习Scala,并遇到了Programming in Scala教科书中的一小段代码,我不太理解。希望有人能帮助我吗?

这来自于Programming in Scala,第二版的Listing 9.1。

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)) // ???

我对带有// ???的代码行有些困惑。使用_是否会创建一个匿名函数,该函数传递给filesMatching?还是说_与此无关,而编译器看到filesMatching需要一个函数,因此不将_.endsWith(query)执行为表达式,而是将其作为函数执行?


1
上面没有任何带有 // ??? 的行。也许你忘记添加了? - Daniel C. Sobral
抱歉,我已经添加了 // ??? - C0deAttack
可能是[Scala _占位符(这段代码如何工作?)]的重复问题(https://dev59.com/71nUa4cB1Zd3GeqPdcHx) - Péter Török
3个回答

14

扩展定义

匿名函数在其更冗长和完整的形式中被定义为:

(a: A, b: B, ...) => function body //using a, b, ...

例如。

(a: String, b: String) => a ++ b // concatenates 2 Strings

推断类型

如果上下文提供了所需的信息(例如,当高阶函数期望其函数参数具有特定的签名时),您可以省略参数类型,如下:

(a, b, ...) => function body //using a, b, ...

例如。

val l = List(1, 2, 3)

//you can omit the type because filter on List[Int] expects a (Int => Boolean)
l.filter(i => i < 3)

占位符语法

最后,如果你的参数在函数体中按照声明顺序被使用一次,你可以使用更短的形式,如下:

_ ++ _ // which is equivalent to (a, b) => a ++ b

每个_都是该函数参数的占位符。

例如:

filesMatching的参数是一个类型为String => Boolean的函数,因此您可以使用

_.endsWith(query) // equivalent to (s: String) => s.endsWith(query)
_.contains(query) // equivalent to (s: String) => s.contains(query)
_.matches(query)  // equivalent to (s: String) => s.matches(query)

3
在这里使用的下划线(_)是函数参数的简写。因此,filesMatching(_.endsWith(query))等同于filesMatching(f => f.endsWith(query))。由于filesMatching接受一个String => Boolean类型的函数作为参数,编译器可以推断f在这里应该是一个String类型。所以你说的没错,这个表达式是一个匿名函数。

0

这种操作最好通过定义函数类型来完成。我在这里找到了一个很好的演示。结合这篇文章,演示应该能够澄清将函数作为参数传递的最佳实践。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接