我在 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 函数时大括号中发生了什么?我是否超越了它?
此外,我尝试在函数声明中交换 func
和 iter
参数。这样我就不能再在第二次调用中使用大括号并插入代码,因为我不知道将 iter 参数放在哪里。
答案 0 :(得分:1)
您的 func
参数的类型是一个函数 - 它的类型是 (String, String) -> String
这意味着它需要两个 String
参数(在括号中)并返回一个 String
(在箭头)。
这就是所有函数类型的样子,(x) -> y
。不带参数且不返回值的函数类型具有类型 () -> Unit
(因为在 Kotlin 中每个函数都返回 something,Unit
是“没有意义的值”输入)。
因此对于您的 func
参数,您可以传入具有该类型的任何函数 - 接受两个字符串,返回一个字符串。您的 concatenation
函数与此匹配,因此您可以将其传入 - 您使用的是函数引用,::concatenation
是传递该函数的一种方式 - 它是对它的引用。
第二次调用 iterate
时,您将创建一个函数对象来传入,而不是引用一个在类中声明为实际方法的函数对象。您正在使用带有两个参数 a
和 b
的 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.
}
这不是语言中的关键字;它只是一个普通函数,将其动作作为最后一个参数。
所以我打赌您现在可以看到上一个问题的答案了。如果要交换 iter
和 func
参数,则必须这样调用:
iterate("Another", "One", {a, b -> a + b}, 6)
(因为 func
不再是最后一个参数,它现在必须在括号内。)
(* 还有其他方法可以指定字符串参数和函数参数,但这里没有空间一一详述!)