Что означает доход (i) в Kotlin? - PullRequest
1 голос
/ 17 марта 2020

Я изучаю сопрограммы Kotlin.

Код А взят из художественного https://kotlinlang.org/docs/reference/coroutines/flow.html

Что означает yield(i) в Kotlin?

Код A

fun foo(): Sequence<Int> = sequence { // sequence builder
    for (i in 1..3) {
        Thread.sleep(100) // pretend we are computing it
        yield(i) // yield next value
    }
}

fun main() {
    foo().forEach { value -> println(value) } 
}

Ответы [ 2 ]

2 голосов
/ 17 марта 2020

Это не функция сопрограммы. Этот код является примером для сравнения с сопрограммами потоков. Последовательности являются итераторами, которые лениво оценивают каждый элемент. Список содержит все значения заблаговременно, но последовательность вычисляет каждое значение только тогда, когда оно запрашивается в чем-то вроде forEach вызова (функция последовательности «терминал»).

Приведенный выше код просто моделирует последовательность это занимает одну десятую секунды, чтобы вычислить каждое из его значений. Он просто выдает числа 1, 2 и 3. yield() - это то, как функция построителя sequence сообщает о получении другого значения, которое нужно использовать.

Функция построителя sequence позволяет передавать лямбду он вычисляет значения по одному и возвращает их, вызывая yield().

. Чаще всего используется не конструктор, а преобразование некоторой коллекции в последовательность с помощью toSequence(), а затем вы можете связать такие операции, как map в последовательности, и они не будут оцениваться до тех пор, пока не будет вызвана операция терминала, подобная forEach. Это позволяет избежать выделения промежуточных списков для каждой операции.

0 голосов
/ 21 марта 2020

Вызов yield(something) в sequence означает iterator of the Sequence hasNext(), и вы получите something при вызове next().

После того, как пользователь итератора вызовет next(), а затем hasNext() снова, выполнение возобновится с yield.


Это выглядит бессмысленно, когда он просто дает 1 2 3. Но рассмотрите возможность обхода двоичного дерева. Предварительный заказ, заказ или пост-заказ очень легко написать в рекурсивном коде. Мы можем использовать стек вызовов, чтобы запомнить, что делать дальше.

Когда мы хотим создать итератор, мы теряем все эти хорошие вещи. Мы должны использовать явный стек, чтобы запомнить, какие узлы следует посетить дальше - если только мы не используем функцию sequence.

та же самая дополнительная проблема является примером такой силы.


Я не согласен с другим ответом о том, что «это не функция сопрограммы». Возможность возобновления в середине выполнения - это определение подпрограмм co . Сравните это с подпрограммами sub , которые должны запускаться с самого начала.

Мы очень часто используем «сопрограммы» для ссылки на конструкции параллелизма в Kotlin. Построение последовательности не является частью этого, но все еще является примером сопрограмм.

...