Skip to content

Instantly share code, notes, and snippets.

@programaker
Last active October 28, 2021 17:35
Show Gist options
  • Save programaker/3caec5646cd9b305f2c3c97f9dfee012 to your computer and use it in GitHub Desktop.
Save programaker/3caec5646cd9b305f2c3c97f9dfee012 to your computer and use it in GitHub Desktop.
Scala List x LazyList x Iterator x View
import scala.util.Try
def compute(n: Int): Int =
val res = n * 10
println(s">>> computing $n -> $res")
res
val n = 4
val nth = 1
///
val list = Iterator.from(1).take(n).toList
// List is eager; the computation happens here and the collection will be traversed 3 times, once per `map`
val list2 = list.map(compute).map(compute).map(compute)
// As the entire List is already computed, we can get the nth element multiple times
val get1 =
Try(list2(nth))
.flatMap(_ => Try(list2(nth)))
.flatMap(_ => Try(list2(nth)))
.flatMap(_ => Try(list2(nth)))
/*
>>> computing 1 -> 10
>>> computing 2 -> 20
>>> computing 3 -> 30
>>> computing 4 -> 40
>>> computing 10 -> 100
>>> computing 20 -> 200
>>> computing 30 -> 300
>>> computing 40 -> 400
>>> computing 100 -> 1000
>>> computing 200 -> 2000
>>> computing 300 -> 3000
>>> computing 400 -> 4000
val list: List[Int] = List(1, 2, 3, 4)
val list2: List[Int] = List(1000, 2000, 3000, 4000)
val get1: scala.util.Try[Int] = Success(2000)
*/
///
val lazyList = LazyList.from(1).take(n)
// LazyList is, well, lazy; nothing happens here
val lazyList2 = lazyList.map(compute).map(compute).map(compute)
// When we get the nth element, ```only the first n elements``` will be computed (and cached!)
// Due to lazyness, the collection will be traversed only once; the 3 `maps` are applied in the same step
// We can get the nth element multiple times, but the necessary computations won't happen again
val get2 =
Try(lazyList2(nth))
.flatMap(_ => Try(lazyList2(nth)))
.flatMap(_ => Try(lazyList2(nth)))
.flatMap(_ => Try(lazyList2(nth)))
/*
>>> computing 1 -> 10
>>> computing 10 -> 100
>>> computing 100 -> 1000
>>> computing 2 -> 20
>>> computing 20 -> 200
>>> computing 200 -> 2000
val lazyList: LazyList[Int] = LazyList(1, 2, <not computed>)
val lazyList2: LazyList[Int] = LazyList(1000, 2000, <not computed>)
val get2: scala.util.Try[Int] = Success(2000)
*/
///
val iterator = Iterator.from(1).take(n)
// Iterator is lazy; nothing happens here
val iterator2 = iterator.map(compute).map(compute).map(compute)
// When we get the nth element, ```only the first n elements``` will be computed
// Due to lazyness, the collection will be traversed only once; the 3 `maps` are applied in the same step
// However, we can get the nth element only once; there's no turning back (and not even an apply method)
val get3 = Try(iterator2.drop(nth).next())
/*
>>> computing 1 -> 10
>>> computing 10 -> 100
>>> computing 100 -> 1000
>>> computing 2 -> 20
>>> computing 20 -> 200
>>> computing 200 -> 2000
val iterator: Iterator[Int] = <iterator>
val iterator2: Iterator[Int] = <iterator>
val get3: scala.util.Try[Int] = Success(2000)
*/
// Repeating the operation will just navigate the Iterator further ahead
// When we reach the end of the Iterator, a `NoSuchElementException` happens
val get31 =
get3
.flatMap(_ => Try(iterator2.drop(nth).next()))
.flatMap(_ => Try(iterator2.drop(nth).next()))
.flatMap(_ => Try(iterator2.drop(nth).next()))
/*
>>> computing 3 -> 30
>>> computing 30 -> 300
>>> computing 300 -> 3000
>>> computing 4 -> 40
>>> computing 40 -> 400
>>> computing 400 -> 4000
val get31: scala.util.Try[Int] = Failure(java.util.NoSuchElementException: next on empty iterator)
*/
///
val view = Iterator.from(1).take(n).toList.view
// View is lazy; nothing happens here
val view2 = view.map(compute).map(compute).map(compute)
// When we get the nth element, ```only the nth element itself``` will be computed
// Due to lazyness, the collection will be traversed only once; the 3 `maps` are applied in the same step
// We can get the nth element multiple times, however, there is no cache and the computation will happen again
val get4 =
Try(view2(nth))
.flatMap(_ => Try(view2(nth)))
.flatMap(_ => Try(view2(nth)))
.flatMap(_ => Try(view2(nth)))
/*
>>> computing 2 -> 20
>>> computing 20 -> 200
>>> computing 200 -> 2000
>>> computing 2 -> 20
>>> computing 20 -> 200
>>> computing 200 -> 2000
>>> computing 2 -> 20
>>> computing 20 -> 200
>>> computing 200 -> 2000
>>> computing 2 -> 20
>>> computing 20 -> 200
>>> computing 200 -> 2000
val view: scala.collection.SeqView[Int] = SeqView(<not computed>)
val view2: scala.collection.SeqView[Int] = SeqView(<not computed>)
val get4: scala.util.Try[Int] = Success(2000)
*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment