s1m*_*nw1 12 lambda scoping higher-order-functions kotlin
有相当多的博客文章(像这样)的标准库的用途功能apply/ with/ run/ also/ let可用,使它更容易一点distingish时实际使用的这那些漂亮的功能.
几个星期以来,官方文档甚至最终提供了关于该主题的指导:https://kotlinlang.org/docs/reference/coding-conventions.html#using-scope-functions-applywithrunalsolet
不过,我认为这是相当困难的记忆功能的个人使用的情况下,由函数名.我的意思是,对我来说,它们似乎是可以互换的,为什么不let被称为run例如?
有什么建议?我认为这些名字不是很有表现力,这使得一开始很难看出差异.
Kir*_*man 24
这是一个关于名称似乎如何形成的非正式概述.
"let"表达式将函数定义与受限范围相关联
在像Haskell这样的FP语言中,您可以使用let将值绑定到受限范围内的变量,如此
aaa = let y = 1+2
z = 4+6
in y+z
Run Code Online (Sandbox Code Playgroud)
Kotlin中的等效代码(虽然过于复杂)
fun aaa() = (1+2).let { y ->
(4+6).let { z ->
y + z
}
}
Run Code Online (Sandbox Code Playgroud)
典型的用法let是将一些计算的结果绑定到一个范围而不"污染"外部范围.
creater.createObject().let {
if (it.isCorrect && it.shouldBeLogged) {
logger.log(it)
}
}
// `it` is out of scope here
Run Code Online (Sandbox Code Playgroud)
该with函数的灵感with来自Delphi或Visual Basic(可能还有许多其他语言)等语言的语言结构
with关键字是Delphi提供的一种便利,用于引用复杂变量的元素,例如记录或对象.
Run Code Online (Sandbox Code Playgroud)myObject.colour := clRed; myObject.size := 23.5; myObject.name := 'Fred';可以改写:
Run Code Online (Sandbox Code Playgroud)with myObject do begin colour := clRed; size := 23.5; name := 'Fred'; end;
相当于Kotlin
with(myObject) {
color = clRed
size = 23.5
name = "Fred"
}
Run Code Online (Sandbox Code Playgroud)
apply在里程碑阶段(M13)相对较晚地被添加到stdlib.你可以在2015年看到这个问题,用户要求提供这样的功能,甚至建议后来使用的名称"申请".
在问题https://youtrack.jetbrains.com/issue/KT-6903和https://youtrack.jetbrains.com/issue/KT-6094中,您可以看到有关命名的讨论.替代喜欢build并init提出但这个名字apply,由丹尼尔Vodopian建议,最终取得了胜利.
apply类似于with它可以用于初始化构造函数之外的对象.这就是为什么,在我看来,apply也可能被命名with.然而,正如with首先添加到stdlib中的那样,Kotlin开发人员决定不破坏现有代码并以不同的名称添加它.
具有讽刺意味的是,Xtend语言提供了所谓的with-operator=>,它基本上与之相同apply.
also甚至晚apply于版本1.1 添加到stdlib 中.同样,https://youtrack.jetbrains.com/issue/KT-6903包含了讨论.该函数基本上就像apply它需要一个普通的lambda (T) -> Unit而不是一个扩展lambda T.() -> Unit.
提议的名称包括"applyIt","applyLet","on","tap","touch","peek","make".但"也"赢了,因为它不会与任何关键字或其他stdlib函数碰撞,并且它的用法(或多或少)读起来像英语句子.
例
val object = creater.createObject().also { it.initiliaze() }
Run Code Online (Sandbox Code Playgroud)
看起来有点像
创世,创建对象,并也对其进行初始化!
其他STDLIB功能,其用途看有点像英语句子包括takeIf和takeUnless也分别在1.1版本中增加.
最后,该run函数实际上有两个签名.第一个fun <R> run(block: () -> R): R只需要一个lambda并运行它.它主要用于将lambda表达式的结果赋值给顶级属性
val logger = run {
val name = System.property("logger_name")
Logger.create(name)
}
Run Code Online (Sandbox Code Playgroud)
第二个签名fun <T, R> T.run(block: T.() -> R): R是一个扩展函数,它将扩展lambda作为参数,并且由于对称原因,它似乎也被命名为"run".它还"运行"lambda但在扩展接收器的上下文中
val result = myObject.run {
intitialize()
computeResult()
}
Run Code Online (Sandbox Code Playgroud)
我不知道命名的任何历史原因.
添加到@kirillRakhman答案:
命名过程的主要部分是(仍然是)主要用例的流利阅读经验.
with:
with(database) {
open()
send()
close()
}
Run Code Online (Sandbox Code Playgroud)
apply:
val v = View().apply {
width = 3.0
height = 4.0
register(this)
}
Run Code Online (Sandbox Code Playgroud)
also:
db.users()
.filter { it.age > 18 }
.map { account }
.also { log(it) }
Run Code Online (Sandbox Code Playgroud)
恕我直言,它并没有真正起作用let.毕竟,它取自"那些可怕的FP语言".但我经常认为它是一种Let's do this!结构.如下所示,您可以将代码读作let's print it!:
account.map { it.owner }.sumBy {age}.let { print(it) }
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
837 次 |
| 最近记录: |