Clojure Lazy Sequence: Эквиваленты в Котлине - PullRequest
0 голосов
/ 07 июня 2018

Clojure предоставляет средства для отложенной оценки значений в (бесконечных) последовательностях.При этом значения будут вычисляться только тогда, когда они фактически потребляются.

Пример бесконечной последовательности одного повторяющегося элемента:

(take 3 (repeat "Hello StackOverflow")) 
//=> ("Hello StackOverflow" "Hello StackOverflow" "Hello StackOverflow")

Использование take помогает потреблять только столько элементов из последовательности, сколько мы хотим.Без него OutOfMemoryError быстро убил бы процесс.

Другим примером бесконечной последовательности является следующий:

(take 5 (iterate inc 1)) 
//(1 2 3 4 5)

Или более продвинутая последовательность, обеспечивающая функцию факториала:

((defn factorial [n]
   (apply * (take n (iterate inc 1)))) 5)

Предоставляет ли Котлин аналогичные последовательности?Как они выглядят?

Я сам ответил на вопрос, чтобы документировать знания здесь.Это нормально согласно Могу ли я ответить на свой вопрос?

1 Ответ

0 голосов
/ 07 июня 2018

В 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, которые позволят нам накопитьзначения легко.

...