In a for-comprehension, I can't just put a print statement:
def prod (m: Int) = { for (a <- 2 to m/(2*3); print (a + " "); b <- (a+1) to m/a; c = (a*b) if (c < m)) yield c }
but I can circumvent it easily with a dummy assignment:
def prod (m: Int) = { for (a <- 2 to m/(2*3); dummy = print (a + " "); b <- (a+1) to m/a; c = (a*b) if (c < m)) yield c }
Being a side effect, and only used (so far) in code under development, is there a better ad hoc solution?
Is there a serious problem why I shouldn't use it, beside being a side effect?
From the discussion with Rex Kerr, the necessity has risen to show the original code, which is a bit more complicated, but did not seem to be relevant for the question (2x .filter, calling a method in the end), but when I tried to apply Rex' pattern to it I failed, so I post it here:
def prod (p: Array[Boolean], max: Int) = { for (a <- (2 to max/(2*3)). filter (p); dummy = print (a + " "); b <- (((a+1) to max/a). filter (p)); if (a*b <= max)) yield (em (a, b, max)) }
Here is my attempt -- (b * a).filter is wrong, because the result is an int, not a filterable collection of ints:
// wrong: def prod (p: Array[Boolean], max: Int) = { (2 to max/(2*3)).filter (p).flatMap { a => print (a + " ") ((a+1) to max/a).filter (p). map { b => (b * a).filter (_ <= max).map (em (a, b, max)) } } }
Part II belongs to the comments, but can't be read, if written there - maybe I delete it in the end. Please excuse.
Ok - here is Rex last answer in code layout:
def prod (p: Array[Boolean], max: Int) = { (2 to max/(2*3)).filter (p).flatMap { a => print (a + " ") ((a+1) to max/a).filter (b => p (b) && b * a < max).map { b => (m (a, b, max)) } } }
Scala offers a lightweight notation for expressing sequence comprehensions. Comprehensions have the form for (enumerators) yield e , where enumerators refers to a semicolon-separated list of enumerators. An enumerator is either a generator which introduces new variables, or it is a filter.
Comprehensions is a construct in Scala which allows us to evaluate certain expressions. The For Comprehension has the form for (enumerators) yield e , where enumerators refer to a semi-colon separated list of enumerators. An enumerator can either be a generator that iterates the list or a filter.
yield keyword will returns a result after completing of loop iterations. The for loop used buffer internally to store iterated result and when finishing all iterations it yields the ultimate result from that buffer.
Finally, we discovered that a for-comprehension is just syntactic sugar for a sequence of calls to methods foreach, map, flatMap, and withFilter.
Starting Scala 2.13
, the chaining operation tap
, has been included in the standard library, and can be used with minimum intrusiveness wherever we need to print some intermediate state of a pipeline:
import util.chaining._ def prod(m: Int) = for { a <- 2 to m / (2 * 3) b <- (a + 1) to (m / a.tap(println)) // <- a.tap(println) c = a * b if c < m } yield c prod(20) // 2 // 3 // res0: IndexedSeq[Int] = Vector(6, 8, 10, 12, 14, 16, 18, 12, 15, 18)
The tap
chaining operation applies a side effect (in this case println
) on a value (in this case a
) while returning the value (a
) untouched:
def tap[U](f: (A) => U): A
It's very convenient when debugging as you can use a bunch of tap
s without having to modify the code:
def prod(m: Int) = for { a <- (2 to m.tap(println) / (2 * 3)).tap(println) b <- (a + 1) to (m / a.tap(println)) c = (a * b).tap(println) if c < m } yield c
This is how you need to write it:
scala> def prod(m: Int) = { | for { | a <- 2 to m / (2 * 3) | _ = print(a + " ") | b <- (a + 1) to (m / a) | c = a * b | if c < m | } yield c | } prod: (m: Int)scala.collection.immutable.IndexedSeq[Int] scala> prod(20) 2 3 res159: scala.collection.immutable.IndexedSeq[Int] = Vector(6, 8, 10, 12, 14 , 16, 18, 12, 15, 18)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With