Kotlin中的交换功能

Aks*_*tel 37 kotlin

是否有更好的方法在kotlin中编写通用交换函数,而不是如何在Java中编写基本交换函数中描述的java方式.

是否有任何kotlin语言功能可以使通用交换功能更简洁直观?

hol*_*ava 52

根本不需要Kotlin中的交换功能.您可以使用现有的功能,例如:

var a = 1
var b = 2

a = b.also { b = a }

println(a) // print 2
println(b) // print 1
Run Code Online (Sandbox Code Playgroud)

  • 如果我试着过于努力地思考它实际上是如何工作的(尽管实际上并不复杂),这会让我头疼.当你阅读它时它确实有意义! (34认同)
  • 还有一个简单的解释.这个语法是`a = b.also({b = a})`的简写.我们看到`also()`方法在`b`上调用,参数为lambda` {b = a}`.`also`只调用它的参数,然后返回`this`.所以先执行lambda` {b = a}`,然后将`a`赋给`also()`的结果,这是它的"this",这是初始的`b`. (5认同)
  • 惊人的!它甚至适用于数组元素:`a[i] = a[j].also { a[j] = a[i] }` (4认同)
  • @Anton3,你的解释是我见过的最好的解释,但我仍然不清楚。你说 lambda 首先被执行。这意味着 b 被分配了 a 的值,因此 b 被覆盖。这里正在进行一些隐式缓冲。我反编译了 a = b.also { b = a } 的字节码,并在 Java 中得到了以下内容,这是传统的交换算法: int a = 1; 整数 b = 2; 字节 var4 = b; b = a; a = var4;那么 b 的缓冲从概念上来说是在何时何地发生的呢?堆?关闭? (2认同)
  • @Phil 在 Kotlin 或 Java 中,每当参数传递给函数、返回或在闭包中捕获时,都会创建一个副本(对于对象类型,我们复制对 GC 管理对象的引用)。然而,Kotlin 在该系统中用“var”捕获打了一个小洞,从概念上讲,它是通过将值放入包装对象中来实现的,只要我们有对内部值(变量名称)的引用,我们就可以改变内部值(变量名称)包装对象。 (2认同)
  • 因此,在示例中,我们首先创建 lambda 对象,它捕获 `a` 和 `var b` 的副本(概念上按照上述机制工作)。其次,我们调用扩展函数“also”,向其传递 lambda(准确地说,是 lambda 对象引用的副本),以及“b”的副本作为隐藏的“this”参数。第三,“also”调用 lambda,它会改变“var b”,然后返回“this”,这是“b”的初始值。 (2认同)

Ruc*_*oom 11

如果你想写一些非常可怕的代码,你可以有这样的函数:

inline operator fun <T> T.invoke(dummy: () -> Unit): T {
    dummy()
    return this
}
Run Code Online (Sandbox Code Playgroud)

这将允许您编写这样的代码

a = b { b = a }
Run Code Online (Sandbox Code Playgroud)

请注意,我建议这样做.只是表明它是可能的.

  • 绝对不是更直观.这是**坏**代码.我只是玩得很​​开心. (7认同)
  • 更简洁:是的。更直观:...也许如果我忘记了我在编程方面学到的一切。:) (3认同)

gav*_*rie 9

Kotlin 鼓励在可能的情况下使用不可变数据(例如使用val代替var)。这大大减少了细微错误的更改,因为如果值不更改,则可以更合理地推理代码。

交换两个值与不可变数据非常相反:我的意思a是交换之前还是之后的值?

考虑以以下不可变方式重写您的代码:

val a = 1
val b = 2

val (a2, b2) = b to a
Run Code Online (Sandbox Code Playgroud)

这通过使用解构声明以及to创建Pair.


Wil*_*zel 9

这是with 的一个很好的用法:

var a = 1
var b = 2

with(a) {
    a = b
    b = this
}

println(a) // 2
println(b) // 1
Run Code Online (Sandbox Code Playgroud)


Lio*_*-On 7

编辑:感谢@hotkey的评论

我相信交换两个变量的代码很简单 - 不要再尝试简化它了.

最优雅的实施形式恕我直言:

var a = 1
var b = 2

run { val temp = a; a = b; b = temp }

println(a) // print 2
println(b) // print 1
Run Code Online (Sandbox Code Playgroud)

优点:

  • 意图是响亮而明确的.没有人会误解这一点.
  • temp 不会留在范围内.

  • 也许`run {val temp = a; a = b; b = temp}`甚至更好一些,因为它更清楚地表明它只是一个单一的动作,并且`temp`也不会泄漏到外部范围内(不会污染完成等). (6认同)
  • 这实际上只是他所说的通用 Java 方式。所以你的答案实际上是“不,没有更好的方法”。我想我倾向于同意。 (3认同)