Iterowalność i Sekwencja Kotlina wyglądają dokładnie tak samo. Dlaczego wymagane są dwa typy?

Oba te interfejsy definiują tylko jedną metodę

public operator fun iterator(): Iterator<T>

Dokumentacja mówi, że {[1] } ma być leniwy. Ale czy Iterable nie jest też leniwy (chyba że poparty Collection)?

Author: hotkey, 2016-02-25

3 answers

Zasadnicza różnica polega na semantyce i implementacji funkcji rozszerzenia stdlib dla Iterable<T> i Sequence<T>.

  • Dla Sequence<T> funkcje rozszerzenia wykonują leniwie, jeśli to możliwe, podobnie jak operacje strumieni Javy pośredniczące. Na przykład, Sequence<T>.map { ... } zwraca inną Sequence<R> i nie przetwarza elementów, dopóki nie zostanie wywołana operacjaterminal , taka jak toList lub fold.

    Rozważ to kod:

    val seq = sequenceOf(1, 2)
    val seqMapped: Sequence<Int> = seq.map { print("$it "); it * it } // intermediate
    print("before sum ")
    val sum = seqMapped.sum() // terminal
    

    Drukuje:

    before sum 1 2
    

    Sequence<T> jest przeznaczony do leniwego użytkowania i wydajnego pipeliningu, gdy chcesz maksymalnie ograniczyć pracę wykonywaną w Terminal , podobnie jak strumienie Java. Lenistwo wprowadza jednak pewne narzuty, co jest niepożądane dla zwykłych prostych przekształceń mniejszych zbiorów i czyni je mniej wydajnymi.

    Ogólnie rzecz biorąc, nie ma dobrego sposobu, aby określić, kiedy jest to potrzebne, więc w kotlinie stdlib lenistwo jest jest jawny i wyodrębniony do interfejsu Sequence<T>, aby uniknąć używania go na wszystkich Iterables domyślnie.

  • Dla Iterable<T>, przeciwnie, funkcje rozszerzenia z semantyką operacji pośredni działają chętnie, przetwarzają elementy od razu i zwracają inne Iterable. Na przykład, Iterable<T>.map { ... } zwraca List<R> z wynikiem odwzorowania.

    Równoważny kod dla Iterable:

    val lst = listOf(1, 2)
    val lstMapped: List<Int> = lst.map { print("$it "); it * it }
    print("before sum ")
    val sum = lstMapped.sum()
    

    To drukuje:

    1 2 before sum
    

    Jak wspomniano powyżej, Iterable<T> domyślnie nie jest leniwy, a to rozwiązanie pokazuje się dobrze: w większości przypadków ma dobre miejsce odniesienia , dzięki czemu wykorzystuje pamięć podręczną procesora, przewidywanie, prefetching itp. tak, że nawet wielokrotne kopiowanie kolekcji nadal działa wystarczająco dobrze i działa lepiej w prostych przypadkach z małymi zbiorami.

    Jeśli potrzebujesz większej kontroli nad rurociągiem ewaluacyjnym, istnieje Jawna konwersja do leniwej sekwencji z Iterable<T>.asSequence() funkcja.

 140
Author: hotkey,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-03-12 11:55:03

Odpowiedź hotkeya:

Ważne jest, aby zauważyć, jak Sekwencja i Iterowalne iteracje w Twoich elementach:

Przykład Sekwencji:

list.asSequence().filter { field ->
    Log.d("Filter", "filter")
    field.value > 0
}.map {
    Log.d("Map", "Map")
}.forEach {
    Log.d("Each", "Each")
}

Wynik logowania:

Filter-Map-Each; filter-Map-Each

Przykład iteracyjny:

list.filter { field ->
    Log.d("Filter", "filter")
    field.value > 0
}.map {
    Log.d("Map", "Map")
}.forEach {
    Log.d("Each", "Each")
}

Filter-filter-Map-Map-Each-Each

 51
Author: Leandro Borges Ferreira,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-12-31 05:39:06

Iterable jest mapowany do interfejsu java.lang.Iterable na JVM, i jest zaimplementowany przez powszechnie używane zbiory, takie jak List lub Gotowi. Funkcje rozszerzenia kolekcji na nich są oceniane chętnie, co oznacza, że wszystkie natychmiast przetwarzają wszystkie elementy w ich wprowadzania i zwraca nową kolekcję zawierającą wynik.

Oto prosty przykład użycia funkcji kolekcji, aby uzyskać nazwiska pierwszych pięciu osób na liście, których wiek wynosi co najmniej 21:

val people: List<Person> = getPeople()
val allowedEntrance = people
    .filter { it.age >= 21 }
    .map { it.name }
    .take(5)

Platforma docelowa: JVMRunning na kotlinie V. 1. 3. 61 najpierw sprawdzenie wieku jest wykonywana dla każdej osoby na liście, a wynik umieszczony w zupełnie nowa lista. Następnie mapowanie ich nazw odbywa się dla każdego Osoba, która pozostała po operatorze filtra, kończąc na jeszcze kolejna nowa lista (to jest teraz List<String>). W końcu jest jeden ostatnia nowa lista utworzona, aby zawierała pięć pierwszych elementów poprzednia lista.

Natomiast sekwencja jest nowym koncepcja w Kotlinie przedstawiająca leniwie oceniany zbiór wartości. Tymi samymi rozszerzeniami kolekcji są dostępne dla interfejsu Sequence, ale te natychmiast zwracają Wystąpienia sekwencji, które reprezentują przetworzony stan daty, ale bez przetwarzania jakichkolwiek elementów. Aby rozpocząć przetwarzanie, Sequence musi być zakończony operatorem terminala, są to zasadniczo wniosek do sekwencji, aby zmaterializować dane to reprezentuje w jakiejś konkretnej formie. Przykłady obejmują toList, toSet, i sum, by wymienić tylko kilka. Gdy są one wywoływane, tylko minimalna wymagana liczba elementów zostanie przetworzona w celu wyprodukowania żądany wynik.

Przekształcenie istniejącego zbioru do sekwencji jest dość prosto, wystarczy użyć rozszerzenia asSequence. Jako wymienione powyżej, należy również dodać operatora terminala, w przeciwnym razie Sekwencja nigdy nie zrobi żadnej obróbki (znowu, leniwy!).

val people: List<Person> = getPeople()
val allowedEntrance = people.asSequence()
    .filter { it.age >= 21 }
    .map { it.name }
    .take(5)
    .toList()

Platforma docelowa: JVMRunning na kotlin V. 1.3.61 w tym przypadku, Osoby w sekwencji są każdorazowo sprawdzane pod kątem ich wieku, jeśli przechodzą, mają swoją nazwę wyodrębnioną, a następnie dodaną do Lista wyników. Powtarza się to dla każdej osoby z oryginalnej listy dopóki nie znajdziemy pięciu osób. W tym momencie funkcja tolista zwraca listę, a reszta osób w Sequence nie jest przetwarzane.

Jest też coś, do czego sekwencja jest zdolna: może zawierać an infinite liczba pozycji. Z tym w perspektywie, to ma sens że operatory działają tak, jak działają - operator na nieskończonej Sekwencja nigdy nie może powrócić, jeśli niecierpliwie wykonuje swoją pracę.

Jako przykład, oto sekwencja, która wygeneruje tyle mocy 2 zgodnie z wymaganiami operatora terminala (pomijając fakt, że to szybko się przepełni):

generateSequence(1) { n -> n * 2 }
    .take(20)
    .forEach(::println)

Więcej znajdziesz tutaj .

 2
Author: Sazzad Hissain Khan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-12-14 19:09:34