package com.zj.example.kotlin.shengshiyuan.helloworld

/**
 * 序列
 * @link https://juejin.im/post/5b28f4946fb9a00e3a5a9b8c
 *
 * 译者有话说
 * 老实说这篇文章，好的地方在于原作者把Kotlin中的序列和Java 8中的流做了一个很好的对比，以及作者给出自己的使用建议以及针对性能效率都是通过实际基准测试结果进行对比的。但是唯一美中不足的是对于Kotlin中哪种场景下使用sequence更好，并没有说的很清楚。关于这点我想补充一下:

 * 1、数据集量级是足够大，建议使用序列(Sequences)。
 * 2、对数据集进行频繁的数据操作，类似于多个操作符链式操作，建议使用序列(Sequences)
 * 3、对于使用first{},last{}建议使用序列(Sequences)。补充一下，细心的小伙伴会发现当你对一个集合使用first{},last{}操作符的时候，我们IDE工具会提示你建议使用序列(Sequences) 代替 集合(Lists)
 * 4、当仅仅只有map操作时，使用sequence

 * 这里只是简单总结了几点，具体详细内容可参考之前三篇有关Kotlin中序列的文章。好了第四篇完美收工。

 * CreateTime: 2019/1/8  18:45
 * @author 郑炯
 */

fun main(args: Array<String>) {
    val demo = `HelloKotlin-Sequences`()
    //demo.test1()
    //demo.test2()
    //demo.test3()
    //demo.test4()
    //demo.test5()
    //demo.test6()
    //demo.test7()
    //demo.test8()
    //demo.test9()
    //demo.test10()
    demo.test11()
}

class `HelloKotlin-Sequences` {

    /**
     * 当你反编译下面代码的时候，你会发现Kotlin编译器会创建三个while循环.其实你可以使用命令式编程方式利用一个
     * 循环就能实现上面相同任务的需求。不幸的是，编译器无法将代码优化到这样的程度。
     *
     * 输出:
     * map
     * map
     * map
     * filter
     * filter
     * filter
     * count
     * count=1
     */
    fun test1() {
        val count = listOf(1, 2, 3)
                .map {
                    println("map")
                    it + 1
                }
                .filter {
                    println("filter")
                    it == 3
                }
                .count {
                    println("count")
                    it == 3
                }

        println("count=$count")
    }

    /**
     * 序列(Sequences) 的秘诀在于它们是共享同一个迭代器(iterator) ---序列允许 map操作 转换一个元素后，
     * 然后立马可以将这个元素传递给 filter操作 ，而不是像集合(lists) 一样等待所有的元素都循环完成了map操作后，
     * 用一个新的集合存储起来，然后又遍历循环从新的集合取出元素完成filter操作。通过减少循环次数，
     * 该序列为我们提供了26％（List为286μs，Sequence为212μs）性能提升
     *
     * 输出:
     * map
     * filter
     * map
     * filter
     * map
     * filter
     * count=0
     */
    fun test2() {
        val count = listOf(1, 2, 3)
                .asSequence()
                .map {
                    println("map")
                    it + 1
                }
                .filter {
                    println("filter")
                    it == 5
                }
                .count {
                    println("count")
                    it == 5
                }
        println("count=$count")
    }

    /**
     * 输出:
     * 什么都不输出
     */
    fun test3() {
        /**
         * 这样其实不是不会执行map和filter中的println, 查看源码可以发现Sequences的map()和filter()方法并不会执行循环操作,
         * 只有当foreach或者count的时候才会执行
         */

        val count = listOf(1, 2, 3)
                .asSequence()
                .map {
                    println("map")
                    it + 1
                }
                .filter {
                    println("filter")
                    it == 5
                }
    }

    /**
     * 使用Sequence后可以提高性能:
     * 当使用接收一个预判断的条件 first or last 方法时候，使用**序列(Sequences)**
     * 会产生一个小的性能提升，如果将它与其他操作符结合使用，它的性能将会得到更大的提升。
     */
    fun test4() {
        listOf(1, 2, 3)
                .asSequence()
                .map { it + 1 }
                .first { it % 100 == 0 }
    }

    fun test5() {
        val list = listOf(1, 2, 3)
                .asSequence()
                .map {
                    it + 1
                    println("map")
                }

        /**
         *
         * 每次forEach上面的map都会重新计算一次，都会重新计算元素。Lists(集合) 中的元素只计算一次，然后存储在内存中。
         * 这就是为什么你不应该将Sequences(序列) 作为参数传递给函数: 函数可能会多次遍历它们。在传递或者在整个使用List之前建议将Sequences(序列) 转换 Lists(集合)
         * 如果你真的想要传递一个Sequences(序列)，你可以使用constrainOnce（） - 它只允许一次遍历Sequences(序列)，第二次尝试遍历会抛出一个异常。
         * 不过，我不会建议这种方法，因为它使代码难以维护。
         */
        list.forEach {
            println("forEach1->" + it)
        }

        println("----------------------------------")
        list.forEach {
            println("forEach2->" + it)
        }
    }

    fun test6() {
        var seq = sequenceOf(1, 2, 3)
        seq.filter { it % 2 == 0 }.toList()
    }

    fun test7() {
        /**
         * generateSequence的意思看源码自己理解
         */
        generateSequence(1, {
            println("----" + it)
            it + 1
        })
                .map {
                    it * 2
                }.take(3)
                .forEach {
                    println(it)
                }
    }

    /**
     * 如果Sequence序列有类似sort这样的函数是有争议的，因为它只是固定空间长度上的惰性，并且不能用于不定长的序列。
     * 之所以引进它是因为它是一种比较受欢迎的函数，并且以这种方式使用它更容易。
     * 但是Kotlin开发人员应该要记住，它不能用于不定长的序列
     *
     * 输出:
     * 0
     * 1
     * 2
     */
    fun test8() {
        generateSequence(0) { it + 1 }.sorted().take(3).toList()
        println("test8")
    }

    /**
     * take后,sequence变成了定长的序列
     */
    fun test9() {
        val list = generateSequence(0) { it + 1 }.take(3).sorted().toList()
        list.forEach(::println)
    }

    /**
     * 仅仅7行即7次操作。这意味着它只要找到第一个元素的那一刻，就会终止整个过程。
     *
     * 输出:
     *
     * In Filter 2
     * In Map 2
     * In Filter 4
     * In Map 3
     * In Filter 6
     * 6
     */
    fun test10() {
        val sequence = sequenceOf(1, 2, 3, 4, 5, 6)
        val result = sequence
                .map { println("In Map $it"); it * 2 }
                .filter { println("In Filter $it");it % 3 == 0 }
        println(result.first())
    }

    /**
     * 一个相对令人惊讶的结果，如Map:Filter:Sum，Sequence比List快得多，而Map:Filter:Average，List比Sequence要快得多。
     *
     * List: 在Sum操作上花费了3.605s，在Average操作上花费了3.536s
     * Sequence: 在Sum操作上花费了1.121s, 在Average操作上花费了1.058s

     * 3605
     * List Map Sum= 1125788928
     * 1121
     * Sequence Map Sum 1125788928
     *
     * 1083
     * List Map Average 2.0000001E7
     * 2222
     * Sequence Map Average 2.0000001E7
     */
    fun test11() {
        val sequence = generateSequence(1) { it + 1 }.take(20000000)
        val list = sequence.toList()

        println("List Map Sum= "
                + measureNanoTime { list.map { it * 2 }.sum() })
        println("Sequence Map Sum "
                + measureNanoTime { sequence.map { it * 2 }.sum() })

        println("List Map Average "
                + measureNanoTime { list.map { it * 2 }.average() })
        println("Sequence Map Average "
                + measureNanoTime { sequence.map { it * 2 }.average() })
    }

    private fun measureNanoTime(map: () -> Number): Number {
        val start = System.currentTimeMillis()
        val result = map()
        println((System.currentTimeMillis() - start))
        return result
    }
}