如果我有Kotlin序列,则每次调用take(n)
都会重新启动序列。
val items = generateSequence(0) {
if (it > 9) null else it + 1
}
@Test fun `take doesn't remember position`() {
assertEquals(listOf(0, 1), items.take(2).toList())
assertEquals(listOf(0, 1, 2), items.take(3).toList())
}
是否有一种简单的写法,another(n)
这样
@Test fun `another does remember position`() {
assertEquals(listOf(0, 1), items.another(2).toList())
assertEquals(listOf(2, 3, 4), items.another(3).toList())
}
我想我必须拥有不是Sequence
的东西才能保持状态,所以也许我实际上要问的是fun Iterator<T>.another(count: Int): List<T>
答案 0 :(得分:2)
Sequence
不记得其位置,但是其iterator
却记得:
val iterator : Iterator<Int> = items.iterator()
现在您所需要的只是take(n)
之类的东西,但Iterator<T>
则如此:
public fun <T> Iterator<T>.another(n: Int): List<T> {
require(n >= 0) { "Requested element count $n is less than zero." }
if (n == 0) return emptyList()
var count = 0
val list = ArrayList<T>(n)
for (item in this) {
list.add(item)
if (++count == n)
break
}
return list
}
答案 1 :(得分:1)
那呢:
@Test
fun `another does remember position`() {
val items: Sequence<Int> = generateSequence(0) {
if (it > 9) null else it + 1
}
val (first, rest) = items.another(2)
assertEquals(listOf(0, 1), first.toList())
assertEquals(listOf(2, 3, 4), rest.another(3).first.toList())
}
fun <T> Sequence<T>.another(n: Int): Pair<Sequence<T>, Sequence<T>> {
return this.take(n) to this.drop(n)
}
答案 2 :(得分:1)
要回答问题的最后一部分:
我想我必须要拥有不是Sequence才能保持状态的东西,所以也许我真正想要的是对Fun Iterator.another(count:Int):List
这样的实现方式是:
fun <T> Iterator<T>.another(count: Int): List<T> {
val collectingList = mutableListOf<T>()
while (hasNext() && collectingList.size < count) {
collectingList.add(next())
}
return collectingList.toList()
}
如果您使用序列产生的迭代器,则此测试通过:
@Test
fun `another does remember position`() {
val items = generateSequence(0) {
if (it > 9) null else it + 1
}.iterator() //Use the iterator of this sequence.
assertEquals(listOf(0, 1), items.another(2))
assertEquals(listOf(2, 3, 4), items.another(3))
}
对我来说,您所描述的是一个迭代器,因为它使您可以遍历集合或序列等,但也要记住它的最后位置。
注意,上面的实现并未考虑传入的非正数的情况,如果该数大于要迭代的余数,则将返回一个列表,该列表的大小小于。我想您可以自己考虑一下这个练习:-)
答案 3 :(得分:0)
Sequence
不记得其位置,但是其iterator
却记得:
val iterator : Iterator<Int> = items.iterator()
不幸的是,没有用于迭代器的take(n)
,因此要使用stdlib中的迭代器,您需要将iter
包装到Iterable
中:
val iterable : Iterable<Int> = items.iterator().asIterable()
fun <T> Iterator<T>.asIterable() : Iterable<T> = object : Iterable<T> {
private val iter = this@asIterable
override fun iterator() = iter
}
这使itareble.take(n)
记住了它的位置,但是不幸的是,由于标准.take(n)
要求一个元素太多,因此出现了一个错误:
public fun <T> Iterable<T>.take(n: Int): List<T> {
require(n >= 0) { "Requested element count $n is less than zero." }
if (n == 0) return emptyList()
if (this is Collection<T>) {
if (n >= size) return toList()
if (n == 1) return listOf(first())
}
var count = 0
val list = ArrayList<T>(n)
for (item in this) {
if (count++ == n)
break
list.add(item)
}
return list.optimizeReadOnlyList()
}
可以通过一些调整来解决:
public fun <T> Iterable<T>.take2(n: Int): List<T> {
require(n >= 0) { "Requested element count $n is less than zero." }
if (n == 0) return emptyList()
if (this is Collection<T>) {
if (n >= size) return toList()
if (n == 1) return listOf(first())
}
var count = 0
val list = ArrayList<T>(n)
for (item in this) {
list.add(item)
//count++
if (++count == n)
break
}
return list
}
现在你们两个测试都通过了:
@Test fun `take does not remember position`() {
assertEquals(listOf(0, 1), items.take2(2).toList())
assertEquals(listOf(0, 1, 2), items.take2(3).toList())
}
@Test fun `another does remember position`() {
assertEquals(listOf(0, 1), iter.take2(2).toList())
assertEquals(listOf(2, 3, 4), iter.take2(3).toList())
}
答案 4 :(得分:0)
您可以创建一个函数generateStatefulSequence
,该函数创建一个序列,该序列通过使用第二个序列的迭代器提供值来保持其状态。
迭代器被捕获在该函数的结束中。
每次迭代时,返回序列的种子lambda({ i.nextOrNull() }
)都从迭代器提供的下一个值开始。
// helper
fun <T> Iterator<T>.nextOrNull() = if(hasNext()) { next() } else null
fun <T : Any> generateStatefulSequence(seed: T?, nextFunction: (T) -> T?): Sequence<T> {
val i = generateSequence(seed) {
nextFunction(it)
}.iterator()
return generateSequence(
seedFunction = { i.nextOrNull() },
nextFunction = { i.nextOrNull() }
)
}
用法:
val s = generateStatefulSequence(0) { if (it > 9) null else it + 1 }
println(s.take(2).toList()) // [0, 1]
println(s.take(3).toList()) // [2, 3, 4]
println(s.take(10).toList()) // [5, 6, 7, 8, 9, 10]
答案 5 :(得分:0)
以下是对fun Iterator<T>.another(count: Int): List<T>
的一个很好的定义:
fun <T> Iterator<T>.another(count: Int): List<T> =
if (count > 0 && hasNext()) listOf(next()) + this.another(count - 1)
else emptyList()
答案 6 :(得分:0)
另一种解决方法(类似于上面的the suggestion by Willi Mentzel)是创建一个asStateful()
扩展方法,该方法通过将任何序列包装到{{1}中,将任何序列转换为会记住该位置的序列},它总是产生相同的迭代器。
Iterable
然后您可以做:
class StatefulIterable<out T>(wrapped: Sequence<T>): Iterable<T> {
private val iterator = wrapped.iterator()
override fun iterator() = iterator
}
fun <T> Sequence<T>.asStateful(): Sequence<T> = StatefulIterable(this).asSequence()