Kotlin中的filter()和{}有什么区别
What is the difference between filter () and {} in Kotlin
我正在研究 Kotlin,在理解带括号的过滤器和大括号的过滤器之间的区别时遇到了一些困难。如果我检查过滤器实现,Intellij 会将我重定向到相同的来源。
代码示例:
listOf("john", "dave").filter { name -> name.startsWith("j") }
// versus
// simple predicate function
fun getSimplePredicate(): (String) -> Boolean =
name: String -> name.firstName.startsWith(prefix)
// actual filter call
listOf("john", "dave).filter(getSimplePredicate())
如果我想以某种方式调用 filter { getSimplePredicate() }
怎么办?
有办法吗?有什么区别?提前致谢
filter()
需要一个谓词函数作为参数,即一个接受字符串和 returns 布尔值的函数。
在Kotlin中,如果一个函数(这里是filter)的最后一个参数是一个函数(谓词),那么你可以传递一个lambda,并且lambda可以在括号外。所以
.filter { ... }
与
相同
.filter({ ... })
因此,在您的代码中,{ name -> name.startsWith("j") }
是一个 lambda,它是传递给 filter()
的参数。
函数getSimplePredicate()
returns一个谓词函数。
所以.filter(getSimplePredicate())
等价于.filter({ name.firstName.startsWith(prefix) })
,也就是等价于.filter { name.firstName.startsWith(prefix) }
.
如果 { getSimplePredicate() }
是一个 returns 布尔值的函数,即 getSimplePredicate()
返回布尔值,则 .filter { getSimplePredicate() }
将有效。但它没有,所以它是无效的。
正如 JB Nizet 所提到的,这并非特定于过滤,而是标准的 Kotlin 语法。如果您调用的函数的最后一个参数是 lambda,例如:
list.map({ size -> size * 2 })
然后你可以把lambda移到括号外:
list.map(){ size -> size * 2 }
(这主要是为了允许看起来像新语言语法的函数。但它通常很有用。)并且如果它们中没有任何内容,您可以完全省略括号:
list.map{ size -> size * 2 }
另外,如果一个 lambda 只有一个参数(并且编译器可以推断它的类型),那么您可以将它称为 it
而不是显式命名:
list.map{ it * 2 }
所有四种形式的意思完全相同:它们使用 lambda 调用 map()
函数。 (您会经常看到这类语法快捷方式;它们有助于使代码更易于阅读。)
好的,开始你的代码:
你的第一行有效,但你的谓词函数需要一些调整才能编译。签名很好,但如果没有大括号,定义将无法工作。 (在 Scala 和 Java 中,箭头是区别部分;但我们刚刚看到了如何在 Kotlin 中省略它,因此 每个 lambda 都必须有大括号 。)
此外,您还没有定义 firstName
或 prefix
。我假设我们可以忽略前者,并将后者作为参数提供。稍微简化一下,得到:
fun getSimplePredicate(prefix: String): (String) -> Boolean
= { it.startsWith(prefix) }
并且 通过这些调整,您确实可以使用它来提供过滤谓词,例如:
listOf("john", "dave").filter(getSimplePredicate("j"))
(请注意,这次没有花括号,因为我们没有在这一行中创建 lambda — 函数已经完成了。)
当然,在这种情况下,根据您的第一个示例,直接提供 lambda 实际上更简单!但这说明了原理。
还有一个值得一提的选项,它是一个函数引用。如果您已经有一个可以完成这项工作的函数,则不需要将它放在 lambda 中,而是可以使用 ::
表示法直接引用它。例如:
fun hasValidPrefix(s: String) = s.startsWith("j")
listOf("john", "dave").filter(::hasValidPrefix)
只有在参数类型兼容的情况下才有效,但它稍微简单一些(并且可以生成稍微更有效的字节码)。
这些都有解释in the Kotlin docs。
我正在研究 Kotlin,在理解带括号的过滤器和大括号的过滤器之间的区别时遇到了一些困难。如果我检查过滤器实现,Intellij 会将我重定向到相同的来源。
代码示例:
listOf("john", "dave").filter { name -> name.startsWith("j") }
// versus
// simple predicate function
fun getSimplePredicate(): (String) -> Boolean =
name: String -> name.firstName.startsWith(prefix)
// actual filter call
listOf("john", "dave).filter(getSimplePredicate())
如果我想以某种方式调用 filter { getSimplePredicate() }
怎么办?
有办法吗?有什么区别?提前致谢
filter()
需要一个谓词函数作为参数,即一个接受字符串和 returns 布尔值的函数。
在Kotlin中,如果一个函数(这里是filter)的最后一个参数是一个函数(谓词),那么你可以传递一个lambda,并且lambda可以在括号外。所以
.filter { ... }
与
相同.filter({ ... })
因此,在您的代码中,{ name -> name.startsWith("j") }
是一个 lambda,它是传递给 filter()
的参数。
函数getSimplePredicate()
returns一个谓词函数。
所以.filter(getSimplePredicate())
等价于.filter({ name.firstName.startsWith(prefix) })
,也就是等价于.filter { name.firstName.startsWith(prefix) }
.
{ getSimplePredicate() }
是一个 returns 布尔值的函数,即 getSimplePredicate()
返回布尔值,则 .filter { getSimplePredicate() }
将有效。但它没有,所以它是无效的。
正如 JB Nizet 所提到的,这并非特定于过滤,而是标准的 Kotlin 语法。如果您调用的函数的最后一个参数是 lambda,例如:
list.map({ size -> size * 2 })
然后你可以把lambda移到括号外:
list.map(){ size -> size * 2 }
(这主要是为了允许看起来像新语言语法的函数。但它通常很有用。)并且如果它们中没有任何内容,您可以完全省略括号:
list.map{ size -> size * 2 }
另外,如果一个 lambda 只有一个参数(并且编译器可以推断它的类型),那么您可以将它称为 it
而不是显式命名:
list.map{ it * 2 }
所有四种形式的意思完全相同:它们使用 lambda 调用 map()
函数。 (您会经常看到这类语法快捷方式;它们有助于使代码更易于阅读。)
好的,开始你的代码:
你的第一行有效,但你的谓词函数需要一些调整才能编译。签名很好,但如果没有大括号,定义将无法工作。 (在 Scala 和 Java 中,箭头是区别部分;但我们刚刚看到了如何在 Kotlin 中省略它,因此 每个 lambda 都必须有大括号 。)
此外,您还没有定义 firstName
或 prefix
。我假设我们可以忽略前者,并将后者作为参数提供。稍微简化一下,得到:
fun getSimplePredicate(prefix: String): (String) -> Boolean
= { it.startsWith(prefix) }
并且 通过这些调整,您确实可以使用它来提供过滤谓词,例如:
listOf("john", "dave").filter(getSimplePredicate("j"))
(请注意,这次没有花括号,因为我们没有在这一行中创建 lambda — 函数已经完成了。)
当然,在这种情况下,根据您的第一个示例,直接提供 lambda 实际上更简单!但这说明了原理。
还有一个值得一提的选项,它是一个函数引用。如果您已经有一个可以完成这项工作的函数,则不需要将它放在 lambda 中,而是可以使用 ::
表示法直接引用它。例如:
fun hasValidPrefix(s: String) = s.startsWith("j")
listOf("john", "dave").filter(::hasValidPrefix)
只有在参数类型兼容的情况下才有效,但它稍微简单一些(并且可以生成稍微更有效的字节码)。
这些都有解释in the Kotlin docs。