There is an interesting problem with some colleagues, and I came up with and discussing, and I can’t bow my head to a lazy solution. There is one?
It came while studying functional programming, and we thought about expanding a simple task in terms of a lazy solution
First I will talk about a lazy problem (to which we found an easy fold + flatMap solution)
Say we have a word. Tue
We want to generate all subsets of a word such that:
For "abcd", the result will be:
"," a "," b "," ab "," c "," ac "," bc "," abc "," d "," ad "," bd "," abd "," cd "," acd "," bcd "," abcd "
Basically go char for char and compose the result so far with the current char, always doubling the result.
Question:
Is there a lazy solution to this problem?
Considering:
Can we consume the input stream lazily, having enough data to create only the number of results that we need
Here is my solution so far in Scala:
import org.scalatest.{FreeSpec, Matchers}
class Problem extends FreeSpec with Matchers {
private def solution(word: Stream[Char]) = foldr(compose, Stream(""))(word)
def compose(letter: Char, results: => Stream[String]): Stream[String] = {
results append results.map(word => word + letter)
}
def foldr[A, B](combine: (A, =>B) => B, base: B)(xs: Stream[A]): B =
if (xs.isEmpty) base
else
combine(xs.head, foldr(combine, base)(xs.tail))
"Problem" - {
"Taking 5 elements from the result should evaluate only 3 elements from the initial stream" in {
solution(
Stream('a', 'b', 'c', 'd', 'e', 'f').map(
x => {
println(s"Applying map on element: '$x'")
x
}
)
).take(5).toList shouldBe List("", "f", "e", "fe", "d")
}
}
}
I used the foldr implementation from blogpost , as I understand it, Scala thread is not lazy foldRight?
, , , ,
Applying map on element: 'a'
Applying map on element: 'b'
Applying map on element: 'c'
Applying map on element: 'd'
Applying map on element: 'e'
Applying map on element: 'f'
Stream
, , :
import org.scalatest.{FreeSpec, Matchers}
class Problem extends FreeSpec with Matchers {
private def solution(word: Stream[Char]) = word.foldRight(Stream("")) (add)
def add(letter: Char, results: Stream[String]): Stream[String] = results.flatMap(result => {
println(s"Composing result '$result' with letter: '$letter'")
Stream(result, letter + result)
})
"Problem" - {
"Taking 5 elements from the result should evaluate only 3 elements from the initial stream" in {
solution(
Stream('a', 'b', 'c', 'd', 'e', 'f').map(
x => {
println(s"Applying map on element: '$x'")
x
}
)
).take(5).toList shouldBe List("", "a", "b", "ab", "c")
}
}
}
:
Applying map on element: 'a'
Applying map on element: 'b'
Applying map on element: 'c'
Applying map on element: 'd'
Applying map on element: 'e'
Applying map on element: 'f'
Composing result '' with letter: 'f'
Composing result '' with letter: 'e'
Composing result '' with letter: 'd'
Composing result '' with letter: 'c'
Composing result '' with letter: 'b'
Composing result '' with letter: 'a'
Composing result 'b' with letter: 'a'
Composing result 'c' with letter: 'b'
Composing result 'c' with letter: 'a'
, . . !
Archetypal Paul
private def solution(word: Stream[Char]) =
word.scanLeft((Stream(""), Stream(""))) ((acc, l)=> {
val r = acc._2.map(_ + l)
(r, acc._2 append r)
}).flatMap(_._1)