Calculating prime numbers in Scala: how does this code work?

前端 未结 3 861
爱一瞬间的悲伤
爱一瞬间的悲伤 2020-12-13 11:10

So I\'ve spent hours trying to work out exactly how this code produces prime numbers.

lazy val ps: Stream[Int] = 2 #:: Stream.from(3).filter(i =>
   ps.ta         


        
3条回答
  •  野趣味
    野趣味 (楼主)
    2020-12-13 11:48

    Your explanations are mostly correct, you made only two mistakes:

    takeWhile doesn't include the last checked element:

    scala> List(1,2,3).takeWhile(_<2)
    res1: List[Int] = List(1)
    

    You assume that ps always contains only a two and a three but because Stream is lazy it is possible to add new elements to it. In fact each time a new prime is found it is added to ps and in the next step takeWhile will consider this new added element. Here, it is important to remember that the tail of a Stream is computed only when it is needed, thus takeWhile can't see it before forall is evaluated to true.

    Keep these two things in mind and you should came up with this:

    ps = [2]
    i = 3
      takeWhile
        2*2 <= 3 -> false
      forall on []
        -> true
    ps = [2,3]
    i = 4
      takeWhile
        2*2 <= 4 -> true
        3*3 <= 4 -> false
      forall on [2]
        4%2 > 0 -> false
    ps = [2,3]
    i = 5
      takeWhile
        2*2 <= 5 -> true
        3*3 <= 5 -> false
      forall on [2]
        5%2 > 0 -> true
    ps = [2,3,5]
    i = 6
    ...
    

    While these steps describe the behavior of the code, it is not fully correct because not only adding elements to the Stream is lazy but every operation on it. This means that when you call xs.takeWhile(f) not all values until the point when f is false are computed at once - they are computed when forall wants to see them (because it is the only function here that needs to look at all elements before it definitely can result to true, for false it can abort earlier). Here the computation order when laziness is considered everywhere (example only looking at 9):

    ps = [2,3,5,7]
    i = 9
      takeWhile on 2
        2*2 <= 9 -> true
      forall on 2
        9%2 > 0 -> true
      takeWhile on 3
        3*3 <= 9 -> true
      forall on 3
        9%3 > 0 -> false
    ps = [2,3,5,7]
    i = 10
    ...
    

    Because forall is aborted when it evaluates to false, takeWhile doesn't calculate the remaining possible elements.

提交回复
热议问题