kotlin 内存泄漏问题_Kotlin和Vavr的注水问题

kotlin 内存泄漏问题

我第一次看到以程序解决的浇水问题Martin Odersky在Coursera上进行的关于功能程序设计的精彩演讲 。 该解决方案展示了
使用Scala在Streams中进行惰性评估

使用Kotlin解决注水问题

我想探索如何使用Kotlin重写Martin Odersky所描述的解决方案,我意识到了两件事–一是Kotlin提供的不可变数据结构只是Java Collections库的包装,并且不是真正的不可变,其次是使用Streams的解决方案。 Java中的功能将很困难。 但是, Vavr提供了一个很好的替代方案–一流的Immutable集合库和Streams库,我在用Kotlin和Vavr复制解决方案方面大胆尝试。

杯子看起来像这样,用Kotlin数据类表示

 import io.vavr.collection.List  data class Cup(val level: Int, val capacity: Int) { 
     override fun toString(): String { 
         return "Cup($level/$capacity)" 
     }  } 

由于倒水问题代表了一组杯子的“状态”,因此可以通过以下方式简单地表示为“类型别名”:

 typealias State = List<Cup> 

杯子中的水可以执行3种不同类型的移动-将其倒空,填充或从一个杯子倒入另一个杯子,再次以Kotlin数据类表示:

 interface Move { 
     fun change(state: State): State  }  data class Empty(val glass: Int) : Move { 
     override fun change(state: State): State { 
         val cup = state[glass] 
         return state.update(glass, cup.copy(level = 0 )) 
     } 
     override fun toString(): String { 
         return "Empty($glass)" 
     }  }  data class Fill(val glass: Int) : Move { 
     override fun change(state: State): State { 
         val cup = state[glass] 
         return state.update(glass, cup.copy(level = cup.capacity)) 
     } 
     override fun toString(): String { 
         return "Fill($glass)" 
     }  }  data class Pour(val from: Int, val to: Int) : Move { 
     override fun change(state: State): State { 
         val cupFrom = state[from] 
         val cupTo = state[to] 
         val amount = min(cupFrom.level, cupTo.capacity - cupTo.level) 
         return state 
                 .update(from, cupFrom.copy(cupFrom.level - amount)) 
                 .update(to, cupTo.copy(level = cupTo.level + amount)) 
     } 
     override fun toString(): String { 
         return "Pour($from,$to)" 
     }  } 

该实现利用Vavr的List数据结构的“更新”方法来创建一个仅更新相关元素的新列表。

“路径”表示导致当前状态的移动历史:

 data class Path(val initialState: pour.State, val endState: State, val history: List<Move>) { 
     fun extend(move: Move) = Path(initialState, move.change(endState), history.prepend(move)) 
     override fun toString(): String { 
         return history.reverse().mkString( " " ) + " ---> " + endState 
     }  } 

我使用列表的“ prepend”方法将元素添加到历史记录的开头。 列表的前面是O(1)操作,而列表的后面是O(n),因此是选择。

在给定“状态”的情况下,以下几种更改“状态”的可能举动–

1.清空眼镜–

 ( 0 until count).map { Empty(it) } 

2.装满眼镜–

 ( 0 until count).map { Fill(it) } 

3.从一杯倒入另一杯–

 ( 0 until count).flatMap { from -> 
     ( 0 until initialState.length()).filter { to -> from != to }.map { to -> 
         Pour(from, to) 
     }  } 

现在,所有这些举动都用于从一种状态前进到另一种状态。 考虑假设有2杯容量为4升和9升的杯子,最初装有0升水,表示为“ List(杯子(0/4),Cup(0/9))”,所有可能的移动都使下一组状态杯子如下:

倒水问题

类似地,将这些状态中的每个状态推进到一组新状态将是这样的(以某种简化的形式):

倒水问题

当每个州基于所有可能的移动前进到下一组状态时,可以看到可能路径的爆炸式增长,这就是Vavr的Stream数据结构提供的惰性出现的地方。流中的值仅根据要求计算。

给定一组路径,使用Stream通过以下方式创建新路径:

 fun from(paths: Set<Path>, explored: Set<State>): Stream<Set<Path>> { 
     if (paths.isEmpty) { 
         return Stream.empty() 
     } else { 
         val more = paths.flatMap { path -> 
             moves.map { move -> 
                 val next: Path = path.extend(move) 
                 next 
             }.filter { !explored.contains(it.endState) } 
         } 
         return Stream.cons(paths) { from(more, explored.addAll(more.map { it.endState })) } 
     }  } 

因此,现在有了从初始状态到新状态的潜在路径流,对“目标”状态的解决方案变为:

 val pathSets = from(hashSet(initialPath), hashSet())  fun solution(target: State): Stream<Path> { 
     return pathSets.flatMap { it }.filter { path -> path.endState == target }  } 

涵盖了解决方案,使用此代码进行的测试如下所示:有两个4升和9升容量的杯子,最初装有0升水。 最终的目标状态是使第二个杯子装满6升水:

 val initialState = list(Cup( 0 , 4 ), Cup( 0 , 9 ))  val pouring = Pouring(initialState)  pouring.solution(list(Cup( 0 , 4 ), Cup( 6 , 9 ))) 
     .take( 1 ).forEach { path -> 
         println(path) 
     } 

运行时,这会吐出以下解决方案:

 Fill( 1 ) Pour( 1 , 0 ) Empty( 0 ) Pour( 1 , 0 ) Empty( 0 ) Pour( 1 , 0 ) Fill( 1 ) Pour( 1 , 0 ) Empty( 0 ) ---> List(Cup( 0 / 4 ), Cup( 6 / 9 )) 

以图形表示的解决方案如下所示:

倒水问题

简单地遵循我的github回购中提供的示例的工作版本可能会更容易https://github.com/bijukunjummen/algos/blob/master/src/test/kotlin/pour/Pouring.kt

结论

尽管Kotlin缺乏对本机不可变数据结构的一流支持,但我认为Vavr与Kotlin的结合使解决方案与Scala一样优雅。

翻译自: https://www.javacodegeeks.com/2019/03/water-pouring-problem-kotlin-vavr.html

kotlin 内存泄漏问题

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值