在第二次迭代调用中到底发生了什么?

时间:2021-04-29 21:10:05

标签: function kotlin

我在 Kotlin 中有以下代码:

fun iterate(first: String, second: String, iter: Int, func: (String, String) -> String) {
    for(i in 0..iter)
        println(func(first, second))
}

fun concatenation(one: String, two: String): String {
    return "$one $two";
}

fun main(args: Array<String>) {
    iterate("Me", "You", 5, ::concatenation)
    iterate("Another", "One", 6) {a, b -> a + b}
}

谁能解释在第二次调用 iterate 函数时大括号中发生了什么?我是否超越了它? 此外,我尝试在函数声明中交换 funciter 参数。这样我就不能再在第二次调用中使用大括号并插入代码,因为我不知道将 iter 参数放在哪里。

2 个答案:

答案 0 :(得分:1)

您的 func 参数的类型是一个函数 - 它的类型是 (String, String) -> String 这意味着它需要两个 String 参数(在括号中)并返回一个 String(在箭头)。

这就是所有函数类型的样子,(x) -> y。不带参数且不返回值的函数类型具有类型 () -> Unit(因为在 Kotlin 中每个函数都返回 somethingUnit 是“没有意义的值”输入)。

因此对于您的 func 参数,您可以传入具有该类型的任何函数 - 接受两个字符串,返回一个字符串。您的 concatenation 函数与此匹配,因此您可以将其传入 - 您使用的是函数引用,::concatenation 是传递该函数的一种方式 - 它是对它的引用。

第二次调用 iterate 时,您将创建一个函数对象来传入,而不是引用一个在类中声明为实际方法的函数对象。您正在使用带有两个参数 ab 的 lambda,并对它们调用 +(并隐式返回该值作为结果)。

现在你不是他们在这里String(如果你愿意,你可以明确声明他们的类型),但类型系统基本上是检查这是否适用到两个 String 变量,并且 + 对这些变量的结果也将是 String。所以一切正常,并且可以在您的 iterate 函数中使用。


就像@IR42 在评论中所说的那样,您使用的是 trailing lambda - 基本上当函数调用中的最后一个参数是 lambda 时,您可以将它移到外面(和如果 lambda 是其中唯一的东西,则删除括号)。

但这仅在它是最后一个参数时才有效 - 否则它无法确定您移出了哪个参数! - 这就是为什么您不能更改顺序并保留尾随 lambda 的原因。您可以切换顺序,但调用必须是这样的:

iterate("Me", "You", ::concatenation, 5)
iterate("Another", "One", {a, b -> a + b}, 6)

答案 1 :(得分:1)

您的函数 iterate() 有四个参数:两个 Strings、Int 和一个函数。

当你调用 iterate() 时,你可以通过多种方式给出 String 参数:例如,你可以给出一个包含 String 的变量的名称(例如 one);或者您可以提供文字字符串值(例如 "Me")。

你也可以用这两种方式给函数参数。*

第一种方法的等价物是给现有函数一个 reference。这就是 ::concatenation。 (:: 用于创建函数引用。)

第二种方式的等价物是使用lambda,这是一种写出函数内容的方式。 {a, b -> a + b} 是一个 lambda:它是一个函数,它接受两个参数,并返回它们的连接。 (在这种情况下,编译器可以根据上下文判断两个参数必须是字符串;在其他情况下,您可能需要指定它们的类型。)

这个问题中的代码的棘手之处在于,lambda 并不看起来就像是作为参数传递给函数!它在括号之外:

iterate("Another", "One", 6) {a, b -> a + b}

这是一个特定于 Kotlin 的 feature如果最后一个参数是一个函数,您可以将 lambda 放在右括号之后。 (而且,如果这是唯一的参数,您可以完全省略括号。)

所以它的意思完全一样:

iterate("Another", "One", 6, {a, b -> a + b})

这可能看起来令人困惑,但它支持看起来像新语言语法的函数调用。例如,您可能使用过 with() 函数:

with (someObject) {
    // In here, ‘this’ refers to someObject.
}

这不是语言中的关键字;它只是一个普通函数,将其动作作为最后一个参数。

所以我打赌您现在可以看到上一个问题的答案了。如果要交换 iterfunc 参数,则必须这样调用:

iterate("Another", "One", {a, b -> a + b}, 6)

(因为 func 不再是最后一个参数,它现在必须在括号内。)


(* 还有其他方法可以指定字符串参数和函数参数,但这里没有空间一一详述!)