Kotlin返回后进行实验执行

“Every byte matters in source code”. This is a rule we discussed a lot over lunch breaks. There is a perfect way to write code for given requirements and constraints and sometimes we accept pull requests only because they look “good enough” and we need to get things done — all in all: “good enough” is good enough.

“每个字节在源代码中都很重要”。 这是我们在午休时间讨论很多的规则。 有一种针对给定的要求和约束编写代码的完美方法,有时我们仅接受拉取请求是因为它们看起来“足够好”,而我们需要把事情做好—总而言之:“足够好”就足够了。

But from time to time there pops up a nice example on good looking code without any “code smell”. It don’t want to go into details what “elegant” and “code smell” is, I just want to share the findings for this little example.

但是,有时会弹出一个漂亮的示例,显示漂亮的代码,而没有任何“代码气味”。 它不想详细说明“优雅”和“代码气味”是什么,我只想分享这个小例子的发现。

The scenario:

场景:

We have a function that returns the reference to an object and then needs to update that very reference after it is returned.

我们有一个函数,该函数将对对象的引用返回,然后在返回引用后更新该引用。

Here is a naive and simple implementation of that idea. It doesn’t work. After return Kotlin doesn’t execute code. You get an “unreachable code” warning.

这是该想法的幼稚和简单的实现。 没用 返回后, Kotlin不执行代码。 您会收到“无法访问的代码”警告。

class X {
  var next: X = this  // initialization doesn't matter for the example
  fun forward() {     // this function doesn't work, but shows the intention
    return next
    next = next.next  // this line is "unreachable code" and never becomes executed
  }
}

So the first working approach is simple; lets add a temporary variable to get that up and running. That’s good enough for production…

因此,第一种工作方法很简单; 让我们添加一个临时变量来启动并运行它。 对于生产来说已经足够了……

class X {
  var next: X = this
  fun forward() {
    val ret = next
    next = next.next
    return ret
  }
}

…but it doesn’t look nice, the temp variable is a technical crutch. So lets see if we can get rid of it.

…但是看起来不太好,temp变量是技术上的拐杖。 因此,让我们看看是否可以摆脱它。

Can we (miss?)use try…catch…finally and do the modification in that last finally block?

我们可以(错过吗?)使用try ... catch ... finally ,并在最后一个finally块中进行修改吗?

class X {
  var next: X = this
  fun forward(): X {
    try {
      return next
    } finally {
       next = next.next
    }
  }
}

This works, and… looks a bit better than the temp variable, but we have more lines. We can write it in one line by making it a lambda, but doesn’t make it much better.

这行得通,并且……看起来比temp变量好一点,但是我们有更多行。 我们可以通过使其成为lambda来将其编写为一行,但是并不能使其更好。

class X {
  var next: X = this
  fun forward(): X = try { next } finally { next = next.next }
}

We know that the try is not needed, that code smells at bit. But if we would have exception handling already in the code, we would probably leave it like this.

我们知道不需要尝试 ,该代码有点气味。 但是,如果我们已经在代码中进行了异常处理,则可能会这样。

Lets go back to the original idea and go a bit more crazy. We need a temp variable. There is something in the Kotlin handbook that exactly offers that. I can be found in the documentation for the operator inc(), that is utilized for the special symbol ++.

让我们回到最初的想法,然后变得更加疯狂。 我们需要一个临时变量。 Kotlin手册中确实提供了某些内容。 我可以在运算符inc()文档中找到,该文档用于特殊符号++

Kotlin (as basically all other languages) distinguish between the postfix ++ and the prefix ++. var j = 0; var i = j++ gives us i=0 and j=1. var j = 0; var i = ++j creates i = 1 and j = 1.

Kotlin(基本上是所有其他语言)区分后缀++和前缀++var j = 0; var i = j ++给出i = 0j = 1var j = 0; var i = ++ j创建i = 1j = 1

How does Kotlin do this? Here from that handbook:

Kotlin如何做到这一点? 该手册中的内容如下:

Image for post

And “storage a0” is exactly the temp variable that we can utilize. Lets try it out. Our inc()-operator just needs to return the next element, which is then assigned to itself. Next solution looks like that:

“ storage a0”正是我们可以利用的临时变量。 让我们尝试一下。 我们的inc()运算符只需要返回下一个元素,然后将其分配给它自己。 下一个解决方案如下所示:

class X
  var next: X = this
  fun forward(): X {
    operator fun X.inc(): X = next
    return next++
  }
}

We can make it more elegant be moving the operator definition out of the function. And if we could change the whole interface, make inc() public, and everyone could just call next++ instead of forward().

将运算符定义从函数中移出,我们可以使其更加优雅。 而且,如果我们可以更改整个接口,请将inc()公开,每个人都可以调用next ++而不是forward()

class X
  var next: X = this
  private operator fun X.inc(): X = next
  fun forward(): X = next++
}

Our function a very nice one-liner. But still — introducing an operator and somehow interpret ++ as the same as “move to the next element” smells still a bit…

我们的功能非常出色。 但是仍然-引入一个运算符并以某种方式将++解释为与“移至下一个元素”相同的气味……

The Kotlin syntax is not at its limits here — we have another solution: also for the win! Lets return next, but execute some code also(!) before returning.

这里没有Kotlin语法的局限性-我们还有另一种解决方案: 也是为了胜利! 让我们返回next ,但是在返回之前还要执行一些代码(!)。

class X
  var next: X = this
  fun forward(): X = next.also { next = next.next }
}

This seems the most elegant implementation of code execution “after” return.

这似乎是返回之后“代码执行”的最优雅实现。

And if you want to play around, here a working example with all functions.

而且,如果您想玩转,这里是所有功能的有效示例。

class X(val name: String) {
  var next: X = this


  fun forward(): X {
    return next
    next = next.next
  }


  fun forward1(): X {
    val ret = next
    next = next.next
    return ret
  }


  fun forward2(): X {
    try {
      return next
    } finally {
      next = next.next
    }
  }


  fun forward3(): X = try { next } finally { next = next.next }


  fun forward4(): X {
    operator fun X.inc() : X = next
    return next++
  }


  private operator fun X.inc() : X = next
  fun forward5(): X {
    return next++
  }
  fun forward6(): X = next++


  fun forward7(): X = next.also { next = next.next }
}


fun main() {
  // create a list of elements pointing to the next element and return the first element (last element points to itself, doesn't matter*)
  val x = ('a'..'z')
        .map { X(it.toString()) }
        .zipWithNext { a, b -> a.also { a.next = b }}[0]


  // list of functions that all forward to the next element
  val fList = listOf(X::forward, X::forward1, X::forward2, X::forward3, X::forward4, X::forward5, X::forward6, X::forward7)


  println("Current next name: ${x.next.name}")
  fList.forEach {
    it(x)
    println("Current next after calling ${it.name}: ${x.next.name}")
  }
}

Creates the following output:

创建以下输出:

Current next name: b
Current next after calling forward: b
Current next after calling forward1: c
Current next after calling forward2: d
Current next after calling forward3: e
Current next after calling forward4: f
Current next after calling forward5: g
Current next after calling forward6: h
Current next after calling forward7: i

If you know another way to do it, no matter if more or less elegant, please let me know. Comments welcome. :-)

如果您知道另一种制作方法,则无论高雅程度如何,请让我知道。 欢迎发表评论。 :-)

翻译自: https://medium.com/swlh/kotlin-experiments-execution-after-return-ea919f239c4

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值