This SO answer <https://stackoverflow.com/a/351643...
# functional
b
This SO answer https://stackoverflow.com/a/35164370/1772342 provides a lazy concatenation method by defining a custom extension function on
Sequence
, but why isn't the operand of
Sequence.plus(...)
evaluated lazily by default? Are there any plans to provide this behavior? Is there a way to use the stdlib functions to get a truly lazy implementation for concatenation of Iterables? @h0tk3y
h
breandan: When you use
Sequence.plus(...)
, both left and right operands, I mean
Sequence
instances, are evaluated before the
plus
call (just like any function has its arguments evaluated before it is called). The items from the right operand sequence are evaluated only when the left one finishes, but still the
Sequence
itself is created eagerly (and this is exactly what was preventing us from writing a recursive primes filter in the answer). As far as I can tell, now you can do what is done in the answer using
Sequence { ... }
constructor from stdlib:
Copy code
lazyRight = left + Sequence { createSomeIterator() }
In this case, what's inside the lambda is evaluated only when an item from the second sequence is queried.
b
Is this what you had in mind?
Copy code
fun primesFilter(from: Sequence<Int>): Sequence<Int> = from.iterator().let {
    val current = it.next()

    sequenceOf(current) + Sequence {
        primesFilter(it.asSequence().filter { it % current != 0 }).iterator()
    }
}
I noticed that when benchmarking the two implementations, the
Sequence {...}
constructor takes significantly longer than your
lazyPlus {...}
implementation. Here's the full code:
Copy code
fun primes(): Sequence<Int> {
    fun primesFilter(from: Sequence<Int>): Sequence<Int> = from.iterator().let {
        val current = it.next()
        // h0tk3y's lazy concatenation: 588ms
        sequenceOf(current) lazyPlus {
            primesFilter(it.asSequence().filter { it % current != 0 })
        }

        // stdlib's lazy concatenation: 23923ms
//        sequenceOf(current) + Sequence {
//            primesFilter(it.asSequence().filter { it % current != 0 }).iterator()
//        }
    }

    return primesFilter((2..Int.MAX_VALUE).asSequence())
}

fun main(args: Array<String>) {
    println(measureTimeMillis {  primes().take(2000).toList() })
}
Am I doing this correctly?
h
@breandan Yes, that's what I meant. The benchmark result looks quite strange, I'd say the
+ Sequence { ...iterator() }
solution could take longer because of the overhead
Sequence { ... }
and
.iterator()
introduce creating the objects, but I thought it shouldn't be that much.