【问题标题】:Clojure Lazy Sequences: Equivalents in KotlinClojure 惰性序列:Kotlin 中的等价物
【发布时间】:2018-06-07 19:10:00
【问题描述】:

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)

Kotlin 是否提供类似的序列?它们长什么样?

我自己回答了这个问题,以便在这里记录知识。根据Can I answer my own question?

,这很好

【问题讨论】:

    标签: clojure kotlin lazy-evaluation sequences lazy-sequences


    【解决方案1】:

    在 Kotlin 中,我们也可以使用 Sequences 的惰性求值。为了创建一个序列,我们可以使用generateSequence(有或没有提供seed

    fun <T : Any> generateSequence(
        seed: T?,
        nextFunction: (T) -> T?
    ): Sequence<T> (source)
    

    返回由起始值seed 和函数nextFunction 定义的序列,在每次迭代中调用该函数以根据前一个值计算下一个值。

    下面将展示一些比较 Clojure 和 Kotlin 序列的示例。

    1。来自一个静态值的无限序列的简单take

    Clojure

    (take 3 (repeat "Hello StackOverflow")) 
    

    科特林

    generateSequence { "Hello StackOverflow" }.take(3).toList()
    

    这些非常相似。在 Clojure 中,我们可以使用 repeat,而在 Kotlin 中,它只是 generateSequence,带有一个将永远产生的静态值。在这两种情况下,都使用take 来定义我们要计算的元素数量。

    注意:在 Kotlin 中,我们将生成的序列转换为带有 toList() 的列表


    2。来自动态值的无限序列的简单take

    Clojure

    (take 5 (iterate inc 1))
    

    科特林

    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)
    

    科特林

    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。阶乘

    最后但同样重要的是,让我们看看如何使用 Kotlin 序列解决阶乘问题。首先,让我们回顾一下 Clojure 版本:

    Clojure

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

    我们从一个序列中获取 n 个值,该序列产生一个从 1 开始的递增数字,并在 apply 的帮助下将它们累加。

    科特林

    fun factorial(n: Int) = generateSequence(1) { it.inc() }.take(n).fold(1) { v1, v2 ->
        v1 * v2
    }
    

    Kotlin 提供了fold,让我们轻松积累价值。

    【讨论】:

    • 特别是循环示例显示了 Clojure 对函数式代码的重视程度。 Kotlin 有它自己的强项:使用协程的命令式惯用语。我认为这是针对复杂情况推荐的习语,而不是到处强制使用 FP:buildSequence { val items = listOf("first", "second", "third"); var i = 0; while (true) { yield(items[i++]); i %= items.size } }
    • 关键是命令式成语很容易扩展到任何类型的复杂性,而无需每次都花费精力来找到要定义的正确 FP 原语,就您当前的逻辑而言更简单。这样的原语在整个代码库中往往只有一个用途,但需要读者理解和记住它们
    猜你喜欢
    • 1970-01-01
    • 2012-07-03
    • 1970-01-01
    • 1970-01-01
    • 2011-06-26
    • 2010-12-08
    • 2014-06-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多