Scala Stream function evaluation -
i got following code:
trait stream[+a] { def uncons: option[(a, stream[a])] def foldright[b](z: => b)(f: (a, => b) => b): b = { uncons.map(t => { f(t._1, t._2.foldright(z)(f)) }).getorelse(z) } def exists(p: => boolean) = foldright(false)((x, acc) => acc || p(x)) def forall(p: => boolean) = foldright(true)((x, acc) => p(x) && acc) } object stream { def cons[a](h: => a, t: => stream[a]): stream[a] = new stream[a] { lazy val uncons = some((h, t)) } } then create stream in lazy manner , invoke exists method check stream elements evaluated:
println(stream.cons({println("5"); 1}, stream.cons({println("6"); 2}, stream.cons({println("7"); 3}, stream.cons({println("8"); 4}, stream.empty)))).exists(_ == 1)) and see is:
5 6 7 8 true so elements evaluated in spite of first 1 enough. seem understand why exists acts way does.
then run following code:
println(stream.cons({println("13"); 1}, stream.cons({println("14"); 2}, stream.cons({println("15"); 3}, stream.cons({println("16"); 4}, stream.empty)))).forall(_ < 2)) and see following:
13 14 false so far forall comes across non-satisfying value terminates traversal.
but why forall acts way? what's crucial difference between , exists?
there 2 things consider :
- the type of
acc - the order of
p(x)in boolean expression.
laziness
if change type of acc b, won't able fail-fast (or short-circuit) in either of methods. must know since code extensively uses laziness, variable of type => b evaluated when value required i.e. used in expression. in case, acc future of result computed on stream. future happen if try looking @ it. thus, prevent whole stream evaluated, must prevent future looked at.
short-circuiting in boolean expressions
this order of p(x) matters. in expression a && b, if a false know whole conjunction false, scala won't try evaluating b because it's pointless.
combining two
now happens if 1 of operands lazy expression ? well, if have lazya || b, scala read expression left right , evaluate lazya. in case, lazya represents accumulation of next element , rest of stream. thus, lazya expands a0 :: lazya1, expands a0 :: a1 :: lazya2. therefore end computing whole stream computing left part of boolean binop.
now, if have a && lazyb, expands a && (b0 :: b1 :: lazyb2). see here, a or bi false, return without evaluating right part of statement. happens in forall.
how fix it
the news fix easy : swap order of p(x) , acc : p(x) true, disjunction return without evaluating acc, stopping computation.
def exists(p: => boolean) = foldright(false)((x, acc) => p(x) || acc) output :
5 true
Comments
Post a Comment