在for-comprehension语句中,我不能只放一个print语句:
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
}
,但我可以很容易地绕过它与一个虚拟赋值:
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
}
作为一个副作用,并且(到目前为止)只在开发中的代码中使用,是否有更好的临时解决方案?
除了副作用之外,还有什么严重的问题让我不能使用它吗?
更新显示实际代码,其中调整一个解决方案比预期的更难:
从与Rex Kerr的讨论中,有必要显示原始代码,这有点复杂,但似乎与问题无关(2x .filter,最后调用方法),但是当我试图将Rex的模式应用于它时,我失败了,所以我把它贴在这里:
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)) }
下面是我的尝试——(b * a).filter是错误的,因为结果是一个int型,而不是一个可过滤的int型集合:
// 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))
}
}
}
第二部分属于评论,但无法阅读,如果写在那里-也许我最后删除它。请原谅。
Ok -这是Rex在代码布局中的最后一个答案:
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> 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)
从Scala 2.13
开始,链接操作tap
已经包含在标准库中,可以在需要打印管道中间状态的地方以最小的干扰使用:
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)
tap
链操作对值(在本例中为a
)应用副作用(在本例中为println
),同时返回未受影响的值(a
):
deftap [U](f: (A) => U): A
调试时非常方便,因为您可以使用一堆tap
而无需修改代码:
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
我通常发现这种编码风格很难遵循,因为循环和中间结果等都是相互混合在一起的。我将不使用for循环,而是写成
def prod(m: Int) = {
(2 to m/(2*3)).flatMap { a =>
print(a + " ")
((a+1) to m/a).map(_ * a).filter(_ < m)
}
}
将副作用语句放在for-comprehension语句中(或者实际上放在任何函数的中间)似乎不是一个好的风格,除了调试之外,在这种情况下,您称之为什么并不重要("调试"似乎是一个好名字)。
如果你真的需要,我认为你最好通过分配一个中间值来分隔你的关注点,例如(你的原始布局更漂亮):
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)
}
是
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)
}
}