В Kotlin мы также можем использовать ленивую оценку, используя Sequence .Чтобы создать последовательность, мы можем использовать generateSequence
(с или без предоставления seed
.
fun <T : Any> generateSequence(
seed: T?,
nextFunction: (T) -> T?
): Sequence<T> (source)
Возвращает последовательность, определенную начальным значением seed
и функция nextFunction
, которая вызывается для вычисления следующего значения на основе предыдущего значения на каждой итерации.
Ниже приведено несколько примеров, сравнивающих Clojure с последовательностями Котлина.
1. Простой take
из бесконечной последовательности одного статического значения
Clojure
(take 3 (repeat "Hello StackOverflow"))
Kotlin
generateSequence { "Hello StackOverflow" }.take(3).toList()
Это очень похоже. В Clojure мы можем использовать repeat
, а в Kotlin это просто generateSequence
со статическим значением, которое будет возвращаться навсегда. В обоих случаях take
используется в порядкечтобы определить количество элементов, которые мы хотим вычислить.
Примечание: В Kotlin мы преобразовываем полученную последовательность в список с toList()
2. Простым take
из бесконечной последовательности динамического значения
Clojure
(take 5 (iterate inc 1))
Kotlin
generateSequence(1) { it.inc() }.take(5).toList()
Этот пример немного отличается, поскольку последовательности дают приращение предыдущего значения бесконечно.Kotlin generateSequence
может быть вызван с начальным числом (здесь: 1
) и nextFunction
(с увеличением предыдущего значения).
3.Циклическое повторение значений из списка
Clojure
(take 5 (drop 2 (cycle [:first :second :third ])))
// (:third :first :second :third :first)
Kotlin
listOf("first", "second", "third").let { elements ->
generateSequence(0) {
(it + 1) % elements.size
}.map(elements::get)
}.drop(2).take(5).toList()
В этом примеремы циклически повторяем значения списка, отбрасываем первые два элемента, а затем берем 5. В Kotlin получается довольно многословно, потому что повторять элементы из списка непросто.Чтобы исправить это, простая функция расширения делает соответствующий код более читабельным:
fun <T> List<T>.cyclicSequence() = generateSequence(0) {
(it + 1) % this.size
}.map(::get)
listOf("first", "second", "third").cyclicSequence().drop(2).take(5).toList()
4.Факториал
Наконец, но не в последнюю очередь, давайте посмотрим, как проблема факториала может быть решена с помощью последовательности Котлина.Сначала давайте рассмотрим версию Clojure:
Clojure
(defn factorial [n]
(apply * (take n (iterate inc 1))))
Мы берем n значений из последовательности, которая дает увеличивающееся число, начинающееся с 1, и накапливаем их с помощьюпомощь apply
.
Kotlin
fun factorial(n: Int) = generateSequence(1) { it.inc() }.take(n).fold(1) { v1, v2 ->
v1 * v2
}
Kotlin предлагает fold
, которые позволят нам накопитьзначения легко.