Kotlin 列表尾函数
Kotlin List tail function
我试图在 List<T>
中找到一个尾函数,但我找不到。我最终这样做了。
fun <T> List<T>.tail() = this.takeLast(this.size -1)
有更好的方法吗?
Kotlin 没有内置的 List<T>.tail()
函数,所以实现自己的扩展函数是唯一的方法。虽然你的实现非常好,但可以稍微简化一下:
fun <T> List<T>.tail() = drop(1)
或者,您可以定义扩展而不是扩展函数 属性:
val <T> List<T>.tail: List<T>
get() = drop(1)
val <T> List<T>.head: T
get() = first()
然后像这样使用它:
val list = listOf("1", "2", "3")
val head = list.head
val tail = list.tail
你和@Vladimir Mironov 的解决方案将 工作,但它们会自动创建原始列表(没有第一个元素)的急切副本,这可能需要很长时间对于更大的列表。我会用包装器 List
class 来定义它,将其方法委托给包装器,忽略使用索引调整的第一个元素:
private class TailList<T> (private val list: List<T>) : List<T> {
override val size: Int
get() = list.size -1
override fun isEmpty(): Boolean = size == 0
override fun iterator(): Iterator<T> = listIterator()
override fun listIterator(): ListIterator<T> = list.listIterator(1)
override fun listIterator(index: Int): ListIterator<T> = list.listIterator(index + 1)
override fun subList(fromIndex: Int, toIndex: Int): List<T> = list.subList(fromIndex + 1, toIndex + 1)
override fun lastIndexOf(element: T): Int = list.lastIndexOf(element) - 1
override operator fun get(index: Int): T = list[index + 1]
// The following member functions require the copy of a new list
override fun containsAll(elements: Collection<T>): Boolean = tailList.containsAll(elements)
override fun contains(element: T): Boolean = tailList.contains(element)
override fun indexOf(element: T): Int = tailList.indexOf(element)
private val tailList by lazy { ArrayList(this) } // makes a proper copy the elements this list represents
}
您可能会注意到评论后的部分中的功能仍然最终制作了一个急切的副本。我这样做只是为了简单起见。为了记忆,我做了一个lazy
tailList
属性
它们都可以通过手动迭代 collection 来实现,而不是进行某种委托。如果那是你喜欢的,我相信你能弄明白。
这样,头尾属性就变成了这样:
val <T> List<T>.tail: List<T>
get() =
if(this.isEmpty())
throw IllegalStateException("Cannot get the tail of an empty List")
else
TailList(this)
val <T> List<T>.head: T
get() = this[0] // or first()
如果你真的需要它,我可以添加一个更新来制作最后三个成员函数,这样他们就不会制作急切的副本。
编辑:
注意:如果你遵循 Kotlin 迄今为止遵循的约定,你就不会像这样让 List
的尾巴变得懒惰,因为它们在 List
上的所有函数都会产生急切的副本。相反,特别是如果您使用 head
和 tail
递归迭代列表,我会看看您是否可以以某种方式在 Sequence
上尝试这个包装器想法。 Sequence
存在的全部意义在于 collections 的懒惰工作。
编辑 2:
显然 sublist() 创建了一个视图,因此已经很懒了。本质上,我只是教了你如何为子列表创建实现,除了我把它缩小到尾部。
因此,在这种情况下,只需将 sublist() 用于尾部函数即可。
如果使用非可变列表,它是非常安全的,而且内存消耗更少,只需使用:
fun <T> List<T>.tail(): List<T> =
if (isEmpty()) throw IllegalArgumentException("tail called on empty list")
else subList(1, count())
如果您同时需要头和尾(这是一个基本的序列操作)并且不急于使用流,您可能需要一个序列。
fun <T> Sequence<T>.headTail() =
HeadTailSequence(this)
.let { it.head to it.iterator.asSequence() }
private class HeadTailSequence<T>(parent: Sequence<T>) : Sequence<T> {
val iterator = parent.iterator()
val head: T? = iterator.next()
override fun iterator(): Iterator<T> {
return object : Iterator<T> {
override fun hasNext(): Boolean = iterator.hasNext()
override fun next(): T = if (iterator.hasNext()) throw NoSuchElementException() else iterator.next()
}
}
}
Iterable 的扩展可以类似的方式完成。
我试图在 List<T>
中找到一个尾函数,但我找不到。我最终这样做了。
fun <T> List<T>.tail() = this.takeLast(this.size -1)
有更好的方法吗?
Kotlin 没有内置的 List<T>.tail()
函数,所以实现自己的扩展函数是唯一的方法。虽然你的实现非常好,但可以稍微简化一下:
fun <T> List<T>.tail() = drop(1)
或者,您可以定义扩展而不是扩展函数 属性:
val <T> List<T>.tail: List<T>
get() = drop(1)
val <T> List<T>.head: T
get() = first()
然后像这样使用它:
val list = listOf("1", "2", "3")
val head = list.head
val tail = list.tail
你和@Vladimir Mironov 的解决方案将 工作,但它们会自动创建原始列表(没有第一个元素)的急切副本,这可能需要很长时间对于更大的列表。我会用包装器 List
class 来定义它,将其方法委托给包装器,忽略使用索引调整的第一个元素:
private class TailList<T> (private val list: List<T>) : List<T> {
override val size: Int
get() = list.size -1
override fun isEmpty(): Boolean = size == 0
override fun iterator(): Iterator<T> = listIterator()
override fun listIterator(): ListIterator<T> = list.listIterator(1)
override fun listIterator(index: Int): ListIterator<T> = list.listIterator(index + 1)
override fun subList(fromIndex: Int, toIndex: Int): List<T> = list.subList(fromIndex + 1, toIndex + 1)
override fun lastIndexOf(element: T): Int = list.lastIndexOf(element) - 1
override operator fun get(index: Int): T = list[index + 1]
// The following member functions require the copy of a new list
override fun containsAll(elements: Collection<T>): Boolean = tailList.containsAll(elements)
override fun contains(element: T): Boolean = tailList.contains(element)
override fun indexOf(element: T): Int = tailList.indexOf(element)
private val tailList by lazy { ArrayList(this) } // makes a proper copy the elements this list represents
}
您可能会注意到评论后的部分中的功能仍然最终制作了一个急切的副本。我这样做只是为了简单起见。为了记忆,我做了一个lazy
tailList
属性
它们都可以通过手动迭代 collection 来实现,而不是进行某种委托。如果那是你喜欢的,我相信你能弄明白。
这样,头尾属性就变成了这样:
val <T> List<T>.tail: List<T>
get() =
if(this.isEmpty())
throw IllegalStateException("Cannot get the tail of an empty List")
else
TailList(this)
val <T> List<T>.head: T
get() = this[0] // or first()
如果你真的需要它,我可以添加一个更新来制作最后三个成员函数,这样他们就不会制作急切的副本。
编辑:
注意:如果你遵循 Kotlin 迄今为止遵循的约定,你就不会像这样让 List
的尾巴变得懒惰,因为它们在 List
上的所有函数都会产生急切的副本。相反,特别是如果您使用 head
和 tail
递归迭代列表,我会看看您是否可以以某种方式在 Sequence
上尝试这个包装器想法。 Sequence
存在的全部意义在于 collections 的懒惰工作。
编辑 2: 显然 sublist() 创建了一个视图,因此已经很懒了。本质上,我只是教了你如何为子列表创建实现,除了我把它缩小到尾部。
因此,在这种情况下,只需将 sublist() 用于尾部函数即可。
如果使用非可变列表,它是非常安全的,而且内存消耗更少,只需使用:
fun <T> List<T>.tail(): List<T> =
if (isEmpty()) throw IllegalArgumentException("tail called on empty list")
else subList(1, count())
如果您同时需要头和尾(这是一个基本的序列操作)并且不急于使用流,您可能需要一个序列。
fun <T> Sequence<T>.headTail() =
HeadTailSequence(this)
.let { it.head to it.iterator.asSequence() }
private class HeadTailSequence<T>(parent: Sequence<T>) : Sequence<T> {
val iterator = parent.iterator()
val head: T? = iterator.next()
override fun iterator(): Iterator<T> {
return object : Iterator<T> {
override fun hasNext(): Boolean = iterator.hasNext()
override fun next(): T = if (iterator.hasNext()) throw NoSuchElementException() else iterator.next()
}
}
}
Iterable 的扩展可以类似的方式完成。