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.

Revisions

  1. programaker revised this gist Oct 28, 2021. 1 changed file with 56 additions and 53 deletions.
    109 changes: 56 additions & 53 deletions list_lazylist_iterator_view.scala
    Original file line number Diff line number Diff line change
    @@ -1,8 +1,9 @@
    import scala.util.Try

    def compute(n: Int): Int =
    println(s">>> computing $n")
    n * 10
    val res = n * 10
    println(s">>> computing $n -> $res")
    res

    val n = 4
    val nth = 1
    @@ -20,20 +21,19 @@ val get1 =
    .flatMap(_ => Try(list2(nth)))
    .flatMap(_ => Try(list2(nth)))
    .flatMap(_ => Try(list2(nth)))

    /*
    >>> computing 1
    >>> computing 2
    >>> computing 3
    >>> computing 4
    >>> computing 10
    >>> computing 20
    >>> computing 30
    >>> computing 40
    >>> computing 100
    >>> computing 200
    >>> computing 300
    >>> computing 400
    >>> 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)
    @@ -54,14 +54,13 @@ val get2 =
    .flatMap(_ => Try(lazyList2(nth)))
    .flatMap(_ => Try(lazyList2(nth)))
    .flatMap(_ => Try(lazyList2(nth)))

    /*
    >>> computing 1
    >>> computing 10
    >>> computing 100
    >>> computing 2
    >>> computing 20
    >>> computing 200
    >>> 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)
    @@ -77,29 +76,34 @@ 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 get3 =
    Try(iterator2.drop(nth).next())
    val get31 =
    get3
    .flatMap(_ => Try(iterator2.drop(nth).next()))
    .flatMap(_ => Try(iterator2.drop(nth).next()))
    .flatMap(_ => Try(iterator2.drop(nth).next()))

    /*
    >>> computing 1
    >>> computing 10
    >>> computing 100
    >>> computing 2
    >>> computing 20
    >>> computing 200
    >>> computing 3
    >>> computing 30
    >>> computing 300
    >>> computing 4
    >>> computing 40
    >>> computing 400
    val iterator: Iterator[Int] = <iterator>
    val iterator2: Iterator[Int] = <iterator>
    val get3: scala.util.Try[Int] = Failure(java.util.NoSuchElementException: next on empty iterator)
    >>> 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)
    */

    ///
    @@ -117,20 +121,19 @@ val get4 =
    .flatMap(_ => Try(view2(nth)))
    .flatMap(_ => Try(view2(nth)))
    .flatMap(_ => Try(view2(nth)))

    /*
    >>> computing 2
    >>> computing 20
    >>> computing 200
    >>> computing 2
    >>> computing 20
    >>> computing 200
    >>> computing 2
    >>> computing 20
    >>> computing 200
    >>> computing 2
    >>> computing 20
    >>> computing 200
    >>> 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)
  2. programaker created this gist Oct 28, 2021.
    137 changes: 137 additions & 0 deletions list_lazylist_iterator_view.scala
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,137 @@
    import scala.util.Try

    def compute(n: Int): Int =
    println(s">>> computing $n")
    n * 10

    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
    >>> computing 2
    >>> computing 3
    >>> computing 4
    >>> computing 10
    >>> computing 20
    >>> computing 30
    >>> computing 40
    >>> computing 100
    >>> computing 200
    >>> computing 300
    >>> computing 400
    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
    >>> computing 10
    >>> computing 100
    >>> computing 2
    >>> computing 20
    >>> computing 200
    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)
    // When we reach the end of the Iterator, a `NoSuchElementException` happens
    val get3 =
    Try(iterator2.drop(nth).next())
    .flatMap(_ => Try(iterator2.drop(nth).next()))
    .flatMap(_ => Try(iterator2.drop(nth).next()))
    .flatMap(_ => Try(iterator2.drop(nth).next()))

    /*
    >>> computing 1
    >>> computing 10
    >>> computing 100
    >>> computing 2
    >>> computing 20
    >>> computing 200
    >>> computing 3
    >>> computing 30
    >>> computing 300
    >>> computing 4
    >>> computing 40
    >>> computing 400
    val iterator: Iterator[Int] = <iterator>
    val iterator2: Iterator[Int] = <iterator>
    val get3: 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
    >>> computing 20
    >>> computing 200
    >>> computing 2
    >>> computing 20
    >>> computing 200
    >>> computing 2
    >>> computing 20
    >>> computing 200
    >>> computing 2
    >>> computing 20
    >>> computing 200
    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)
    */