Lambda 如何设计高阶函数

Lambda 如何设计高阶函数,lambda,kotlin,functional-programming,Lambda,Kotlin,Functional Programming,高阶函数的参数为 函数类型或 带有接收器的函数类型 我们习惯于使用kotlin标准数据库中的过滤和: @Test fun `filter example`() { val filtered = listOf("foo", "bar").filter { it.startsWith("f") } assertThat(filtered).containsOnly("foo") } @Test fun `with example`() { va

高阶函数的参数为

  • 函数类型或
  • 带有接收器的函数类型
我们习惯于使用kotlin标准数据库中的过滤和:

@Test
fun `filter example`() {
    val filtered = listOf("foo", "bar").filter {
        it.startsWith("f")
    }

    assertThat(filtered).containsOnly("foo")
}

@Test
fun `with example`() {
    val actual = with(StringBuilder()) {
        append("foo")
        append("bar")
        toString()
    }

    assertThat(actual).isEqualTo("foobar")
}
filter
使用函数类型参数,
with
使用函数类型参数与receiver。因此,lambdas被传递到
filter
使用
it
访问iterable的元素,而lambdas被传递到
使用
this
访问StringBuilder

我的问题是:当我声明自己的高阶函数时,是否有一个经验法则,使用哪种风格(它与此相对)


换言之: 为什么过滤不是这样设计的

inline fun <T> Iterable<T>.filter2(predicate: T.() -> Boolean): List<T> = filter { it.predicate() }

你只是不想总是和接受者一起工作。例如,考虑你的<代码>过滤器<代码>直接在元素上工作,你必须使用<代码>这个限定符,然后在比较中:

val filtered = listOf("foo", "bar").filter2 {
    this == "f"
}
这看起来既奇怪又不自然。这个
指向什么? 您将
的范围更改为指向接收器,如果您想访问“外部”
,它将如下所示:

this@SomeClass.c =="f"
另一个缺点是,您无法命名参数。例如,考虑嵌套的lambda。无论是
还是
这个
都不合适。您必须提供自定义名称

如果你真的想切换到接收方的范围,你应该考虑一下。有些情况是完美的用例,尤其是。对于通常的高阶函数,您根本不希望具有此功能

我认为很难为此制定一个“规则”,但作为初学者,您可以阅读JetBrains关于如何在可用范围函数之间进行选择的内容(
运行
应用
使用
):

您是在块中的多个对象上调用方法,还是将上下文对象的实例作为参数传递?如果是,请使用其中一个函数,该函数允许您访问上下文对象,而不是使用this(也可以使用或let)。如果在块中根本没有使用接收器,也可以使用


我的经验法则如下:

只要有可能需要命名lambda参数,我就使用
(Type)->Unit

如果我确信我不会给它命名(因此从上下文中可以清楚地看出,我操作的所有东西都是
这个
),或者我甚至想禁止命名(生成器?),那么我就使用
类型。->Unit

使用
应用
运行
都使用第二种方法。。。对我来说,这是有道理的:

with(someString) {
  toUpperCase() // operates on someString... already sounds like: "with some string (do) to upper case"
}
someString.run(::println) // run println with someString; actually: someString.run { println(this) } // e.g.: print this [some string]  
// actually I find this a bad sample... I usually use run where the thing to be run is from the receiver... 
SomeComplexObject().apply {
  // apply being similar to a builder
  complex1 = 3 
  complex2 = 4
}.run {
  // fully constructed complex object
  complexOperationWithoutReturnValue() // this method is part of SomeComplexObject
} // discarding everything here...
以下是我使用第二种方法的示例:

fun sendMail(from : String, to : String, msgBuilder : MessageBuilder.() -> Unit)

sendMail("from", "to") {
  subject("subject")
  body("body")
}

使用
it
或一个参数(例如
builder->
),它只会变得更丑陋,并且不会真正为上下文添加内容

第一个是lamabda,第二个是带接收器的lambda。
fun sendMail(from : String, to : String, msgBuilder : MessageBuilder.() -> Unit)

sendMail("from", "to") {
  subject("subject")
  body("body")
}