println in scala for-comprehension
为了理解,我不能只写一个印刷体声明:
1 2 3 4 5 6 7
| 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
} |
但我可以很容易地通过一个虚拟的任务来规避它:
1 2 3 4 5 6 7
| 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的模式应用到它时,我失败了,所以我将它发布在这里:
1 2 3 4 5 6 7 8
| 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)。筛选错误,因为结果是一个int,而不是可筛选的int集合:
1 2 3 4 5 6 7 8 9
| // 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 ))
}
}
} |
。
第二部分属于评论,但不能阅读,如果写在那里-也许我会在最后删除它。请原谅。
好的-这是rex在代码布局中的最后一个答案:
1 2 3 4 5 6 7 8
| 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 ))
}
}
} |
- 包含"dummy"的代码在my repl(scala 2.9.0.1)中运行。比如用prod (20)来称呼它。
- 在实际的代码示例中,((a+1) to max/a).filter(b => p(b) && b*a < max).map{ b => em(a,b,max) }将完成这个技巧。另外,第一个地图应该是平面图。
- 非常感谢。部分原因是,我的错误现在对我来说是显而易见的——过滤器...filter (p)中的布尔数组p使b在表达式中消失,而后面需要它,所以filter (b => p(b))是前进的道路。将过滤器与&& b*a < max结合也很清楚。如果我再搜索4个小时,我就找不到重复的b =>,我想我明天也找不到它,也不找这里。
- 如果你真的想,你可以第二次把它叫做x =>,而不是b =>。它只是需要一个名称的东西;它通过过滤器后恰好是相同的东西,所以我使用了相同的变量。
你需要这样写:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| 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) |
号
- 因此,我可以省略带大括号的分号,并使用下划线作为比命名虚拟对象更规范的方式来表示unused dummy,但我可以使用这两种方法彼此独立。
- @用户未知:是。
- 老实说,我认为dummy = print(a +"")更清楚——如果这是唯一的改变。其他人看到这行,他们会立即知道这是一个用于打印的虚拟变量赋值。当使用下划线时,他们可能会怀疑这是否是他们以前从未见过的普遍下划线的新用法。
- 我发现_版本被清除了。我想是个人品味的问题吧。
- S/清除/清除
从Scala 2.13开始,链接操作tap已包含在标准库中,并且可以在需要打印管道的某些中间状态时以最小的侵入性使用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| 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链接操作对一个值(在本例中为println)应用副作用(在本例中为a),同时返回未接触的值(a:
def tap[U](f: (A) => U): A
号
调试时非常方便,因为您可以使用一堆tap而无需修改代码:
1 2 3 4 5 6 7
| 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 |
。
- tap也可作为scala 2.12的后端端口:github.com/bigwheel/util-backports
把一个有副作用的语句放在一个内以供理解(或者实际上放在任何函数的中间)似乎不是一个好的风格,除了调试之外,在这种情况下,您称它为什么并不重要("debug"似乎是一个好名字)。
如果您真的需要,我认为您最好通过分配一个中间值来稍微分离您的关注点,例如(您的原始布局更漂亮):
1 2 3 4 5 6 7 8
| 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 )
} |
变成
1 2 3 4 5 6 7 8 9 10 11 12
| 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 )
}
} |
。
- 虽然我已经阅读了for循环如何被转换为map/flatmap,但是我对它的工作方式有了错误的印象,并且只注意到使用upper方法时我的印象是错误的。所有的a都打印出来了,但是花了几分钟的时间才执行em (a, b, max)——我以为方法调用是一个接一个地执行的。是的-它只是为了调试丢弃的代码。
我通常发现这种编码方式很难遵循,因为循环和中间结果等都是相互混合的。我会写一些类似的东西,而不是for循环
1 2 3 4 5 6
| def prod (m : Int ) = {
(2 to m/ (2*3)). flatMap { a =>
print (a + "")
((a+ 1) to m/a ). map(_ * a ). filter(_ < m )
}
} |
这也使得添加打印语句变得更加容易。
- 好吧,真正的代码在A、B上有一个额外的过滤器,它不仅产生C,而且产生一个方法结果,这取决于(A、B、M),这意味着我需要一组额外的花括号来捕获B,但不知何故我在正确包装它的过程中迷失了方向。在相反的方向上,我可以在我的外部for中添加花括号,然后在没有虚拟对象的情况下使用print,并将结果变平,但这也变得更难理解,imho。但是对于类似的情况,我必须记住使用map/flatmap方法的花括号。
- @用户未知-(2 to m/(2*3)).filter(f).flatMap { a =>和map(b => g(a,b,m))将执行此操作,其中f是a上的过滤器,g是a、b、m的函数(除非您的意思是不过滤函数的结果,在这种情况下,您需要在内部循环中交换过滤器和映射的顺序)。
- 谢谢。我把我的代码放到这个问题中,只是出于好奇,因为我观察到我的b的一代已经针对(a*b>m)进行了消毒,所以不需要最后一次测试,而且我无法毫无问题地执行您的模式。但更长的结构不知何故阻塞了我的大脑。也许你喜欢解决这个问题。:)