web-dev-qa-db-ja.com

Kotlinリストのテール関数

List<T>でテール関数を見つけようとしていますが、見つかりませんでした。私はこれをやった。

fun <T> List<T>.tail() = this.takeLast(this.size -1)

これを行うより良い方法はありますか?

27

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
41

Yoursと@Vladimir Mironovのソリューションwillは機能しますが、元のリストの熱心なコピーが自動的に作成され(最初の要素が削除されます)、大きなリストの場合は非常に長い時間がかかります。インデックス調整を使用して最初の要素を無視し、そのメソッドをラップされたものに委譲するラッパーListクラスを使用してそれを定義します。

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
}

コメントがまだ熱心なコピーを作成してしまうと、セクションの関数に気付くでしょう。単純化するためだけにこれを行いました。記憶のために、lazytailListプロパティを作成しました

これらはすべて、なんらかの委任を行うのではなく、コレクションを手動で反復することによって実装できます。それがあなたの望むものなら、きっとあなたはそれを理解できると確信しています。

これで、headプロパティとtailプロパティは次のようになります。

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()

本当に必要な場合は、更新を追加して最後の3つのメンバー関数を作成し、それらが熱心なコピーを作成しないようにすることができます。

編集:注:Kotlinがこれまでに従った規則に従っている場合、List上のすべての関数が熱心なコピーを作成するため、このようにListの尾を遅延させないでください。 。代わりに、特にheadtailを使用してリストを再帰的に反復している場合、Sequenceに対してこのラッパーのアイデアをどうにかして試してみることができます。 Sequenceの存在意義はすべて、コレクションの遅延作業にあります。

編集2:どうやらsublist()はビューを作成するため、すでに遅延しています。基本的には、末尾のみに絞り込んだことを除いて、サブリストの実装を作成する方法を説明しました。

したがって、その場合は、tail関数にsublist()を使用します。

2
Jacob Zimmerman

変更不可能なリストを操作する場合、単純に使用する方が完全に安全で、メモリ消費も少なくなります。

fun <T> List<T>.tail(): List<T> =
    if (isEmpty()) throw IllegalArgumentException("tail called on empty list")
    else subList(1, count())
1
incises