println in scala for-comprehension
Solution 1
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)
Solution 2
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
Solution 3
I generally find that style of coding rather difficult to follow, since loops and intermediate results and such get all mixed in with each other. I would, instead of a for loop, write something like
def prod(m: Int) = {
(2 to m/(2*3)).flatMap { a =>
print(a + " ")
((a+1) to m/a).map(_ * a).filter(_ < m)
}
}
This also makes adding print statements and such easier.
Solution 4
It doesn't seem like good style to put a side-effecting statement within a for-comprehension (or indeed in the middle of any function), execept for debugging in which case it doesn't really matter what you call it ("debug" seems like a good name).
If you really need to, I think you'd be better separating your concerns somewhat by assigning an intermediate val, e.g. (your original laid out more nicely):
def prod (p: Array[Boolean], max: Int) = {
for {
a <- (2 to max / (2 * 3)) filter p
debug = print (a + " ")
b <- ((a + 1) to max / a) filter p
if a * b <= max
} yield em(a, b, max)
}
becomes
def prod2 (p: Array[Boolean], max: Int) = {
val as = (2 to max / (2 * 3)) filter p
for(a <- as) print(a + " ")
as flatMap {a =>
for {
b <- ((a + 1) to max / a) filter p
if a * b <= max
} yield em(a, b, max)
}
}
user unknown
scala, java, linux currently not searching a job in Berlin. :)
Updated on June 06, 2022Comments
-
user unknown almost 2 years
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?
update showing the real code, where adapting one solution is harder than expected:
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)) } } }
-
user unknown over 12 yearsWell, the real code has an additional filter on a, on b and yields not just c, but a methods result, depending on (a, b, m) which means, that I need an additional set of curly brackets to capture the b, but somehow I get lost in the process of wrapping it correctly up. In the opposite direction, I can add curly brackets inside my outer
for
, and then useprint
without an dummy, and flatten the result, but that too gets harder to follow, imho. But for similar situations, I have to keep the curly brackets with the map/flatMap approach in mind. -
user unknown over 12 yearsSo I can omit the semicolons with the curly brace, and use the underline as a more canonical way to express
unused dummy
than naming the thing dummy, but I can use both approaches independently from each other. -
huynhjl over 12 yearsHonestly, I think
dummy = print(a + " ")
is clearer - if that's the only change. Somebody else looks at this line and they'll know immediately that it's a dummy variable assignment for printing. When using the underscore, they may wonder if it's a new usage of the pervasive underscore they haven't seen before. -
missingfaktor over 12 yearsI find the
_
version cleared. Matter of personal taste, I guess. -
Rex Kerr over 12 years@user unknown -
(2 to m/(2*3)).filter(f).flatMap { a =>
andmap(b => g(a,b,m))
will do the trick, wheref
is the filter on a, andg
is your function of a,b,m (unless you mean that you do not filter on the result of the function, in which case you need to swap the order of filter and map in the inner loop). -
user unknown over 12 yearsThanks. I put my code into the question, just for curiosity, because I observed that the generation of my
b
already is sanitized against (a*b > m), so the last test is not needed, and than I'm able to perform your pattern without problem. But the more lengthy structure is somehow blocking my brain. Maybe you like to solve it although. :) -
user unknown over 12 yearsAlthough I have read about how a for-loop is to be translated into map/flatMap, I had a wrong impression about how it works, and only observed that my impression was wrong, by using the upper approach. All the
a
s where printed, but then it took minutes, to perform theem (a, b, max)
- I had thought that the method calls were performed one by one. And yes - it was only for debug purpose in a throw-away code. -
seanf about 5 years
tap
is also available as a backport for Scala 2.12: github.com/bigwheel/util-backports