约瑟夫斯问题
我上周的文章是关于解决Kotlin的约瑟夫斯问题的。 为了便于比较,这是我最初编写的版本:
classSoldier(valposition:Int){
varstate=State.Living
lateinitvarnext:Soldier
funsuicide(){
state=State.Dead
}
funisDead()=state==State.Dead
}
enumclassState{
Living,Dead
}
classCircle(privatevalsize:Int,privatevalstep:Int){
privatevalfirst=Soldier(0)
init{
varperson=first
while(person.position<size-1){
person=createNext(person)
}
vallast=person
last.next=first
}
privatefuncreateNext(soldier:Soldier):Soldier{
valnew=Soldier(soldier.position+1)
soldier.next=new
returnnew
}
funfindSurvivor():Soldier{
varsoldier:Soldier=first
varnumberOfDead=0
while(numberOfDead<size-1){
varcount:Int=0
while(count<step){
soldier=nextLivingSoldier(soldier)
count++
}
soldier.suicide()
numberOfDead++
}
returnnextLivingSoldier(soldier)
}
privatefunnextLivingSoldier(soldier:Soldier):Soldier{
varcurrentSoldier=soldier.next
while(currentSoldier.isDead()){
currentSoldier=currentSoldier.next
}
returncurrentSoldier
}
}
帖子以一个开放的问题结尾:代码是正确的方法吗? 特别是:
- 代码是惯用的Kotlin吗?
- 缺少
for
表示与var
while
使用 - 我个人喜欢太多的可变性(
var
)
我收到了来自社区的许多不同反馈,包括JetBrains的Ilya Ryzhenkov,CédricBeust,Peter Somerhoff和GaëtanZoritchak。 多谢你们!
我认为最有趣的是与Gaëtan最初的Gist略有修改的版本:
classSoldier(valposition:Int,varstate:State=State.Living){
funsuicide(){
state=State.Dead
}
funisAlive()=state==State.Living
}
enumclassState{
Living,Dead
}
classCircle(valsize:Int,valstep:Int){
valsoldiers=Array(size,{Soldier(it)}).toList()
funfindSurvivor():Soldier{
varsoldier=soldiers.first()
(2..size).forEach{
(1..step).forEach{
soldier=soldier.nextLivingSoldier()
}
soldier.suicide()
}
returnsoldier.nextLivingSoldier()
}
tailrecprivatefunSoldier.nextLivingSoldier():Soldier=
if(next().isAlive())
next()
else
next().nextLivingSoldier()
privatefunSoldier.next()=soldiers.get(
if(position==size-1)
0
else
position+1
)
}
我非常喜欢此代码,因为它感觉更像Kotlin。 改进很多。
- 代码更短,不会损失任何可读性
- 这是完全的功能,这里只有一个
var
涉及:- 2个
while
循环及其关联的var
计数器已由Int
范围内的简单forEach
替换。 - 链接
Soldier
实例不是通过next()
方法在Soldier
类本身中进行处理,而是通过包含的Circle
。 因此,一个简单的后备数组可以存储它们,并且不需要带有可变变量的自定义代码。
- 2个
- 递归的
nextLivingSoldier()
函数已使用tailrec
进行“注释”,以便编译器运行其优化魔术。 - 该
Soldier
类不知道它的容器Circle
size
,因此使用它的功能已经在内部移动Circle
类为扩展函数的Soldier
类。 这是Kotlin扩展功能的绝佳用法。
这种经验使我坚信,仅阅读一种语言是不够的。 要真正做到这一点,应该采取以下步骤:
- 显然,学习该语言的语法-及其API,
- 用这种语言编写解决方案或应用的代码,
- 要求反馈,
- 阅读,分析和理解反馈,
- 冲洗并重复。
约瑟夫斯问题