How to write non-leaking tail-recursive function using Stream.cons in Scala?

后端 未结 2 2012
星月不相逢
星月不相逢 2020-12-16 15:10

When writing a function operating on Stream(s), there are different notions of recursion. The first simple sense is not recursive on the compiler level, since t

2条回答
  •  不知归路
    2020-12-16 15:48

    A possible workaround is to make the recHelp method not hold reference to the stream head. This can be achieved by passing a wrapped stream to it, and mutating the wrapper to erase the reference from it:

    @tailrec
    final def rec[A](as: Stream[A]): Stream[B] = 
      if (a.isEmpty) Stream.empty              
      else if (someCond) rec(a.tail)          
      else {
        // don't inline and don't define as def,
        // or anonymous lazy wrapper object would hold reference
        val tailRef = new AtomicReference(a.tail)
        someB(a.head) #:: recHelp(tailRef)  
      }
    
    @tailrec
    final def recHelp[A](asRef: AtomicReference[Stream[A]]): Stream[B] = 
      // Note: don't put the content of the holder into a local variable
      rec(asRef.getAndSet(null))
    

    The AtomicReference is just convenience, atomicity is not required in this case, any simple holder object would do.

    Also note that since recHelp is wrapped in a stream Cons tail, therefore it will only be evaluated once, and Cons also takes care of synchronization.

提交回复
热议问题