现在,我花了一段时间来弄清楚为什么我的递归会以某种方式破坏堆栈。这就是造成这个问题的部分:
scala> for {
| i <- List(1, 2, 3)
| j = { println("why am I evaluated?"); 10 } if false
| } yield (i, j)
why am I evaluated?
why am I evaluated?
why am I evaluated?
res0: List[(Int, Int)] = List()
这不是疯了吗?为什么评估j = ...
,如果它以if false
结尾,那么永远不会?
我学到了什么,而不是{ println ... }
你有一个递归调用(和递归防护,而不是if false
)会发生什么。 :其中
为什么?!
答案 0 :(得分:7)
我要走出困境,说接受的答案可以说更多。
这是一个解析器错误。
警卫可以立即跟随生成器,但是否则需要semi
(实际或推断)。
在下文中,res4
的行不应该编译。
scala> for (i <- (1 to 5).toList ; j = 2 * i if j > 4) yield j
res4: List[Int] = List(6, 8, 10)
scala> for (i <- (1 to 5).toList ; j = 2 * i ; if j > 4) yield j
res5: List[Int] = List(6, 8, 10)
当j的val def与i生成器合并以生成对(i,j)
的新生成器时,会发生什么。然后警卫看起来就像是(合成)发电机。
但语法仍然是错误的。语法是我们的朋友!在类型系统之前很久就是我们的BFF。
在res5
的线上,很明显守卫不守卫val def。
更新
将实施错误降级(或升级,具体取决于您的观点)为specification bug。
检查这种用法,如果控件在它之前控制valdef,就像在Perl中那样看起来像尾随,属于你最喜欢的样式检查器的范围。
答案 1 :(得分:3)
如果像这样构建循环,它将解决您的问题:
scala> for {
| i <- List(1, 2, 3)
| if false
| j = { println("why am I evaluated?"); 10 }
| } yield (i, j)
res0: List[(Int, Int)] = List()
for循环中的Scala语法将if语句视为一种过滤器; this tutorial有一些很好的例子。
考虑它的一种方法是强制执行for循环,当你到达if语句时,如果该语句的计算结果为false,则继续循环的下一次迭代。
答案 2 :(得分:2)
当我遇到类似问题时,我会查看反汇编代码的样子(例如,将.class文件提供给JD-GUI)。
这个for-comprehension反汇编代码的开头看起来像这样:
((TraversableLike)List..MODULE$.apply(Predef..MODULE$.wrapIntArray(new int[] { 1, 2, 3 })).map(new AbstractFunction1() { public static final long serialVersionUID = 0L;
public final Tuple2<Object, BoxedUnit> apply(int i) { Predef..MODULE$.println("why am I evaluated?"); BoxedUnit j = BoxedUnit.UNIT;
return new Tuple2(BoxesRunTime.boxToInteger(i),
j);
}
}...//continues
我们可以看到 i 参数中的int数组映射到AbstractFunction1()
方法,apply
方法首先执行println
nomatter,然后将Unit
分配给参数j
,最后返回两个元组(i,j),以进一步将其导入进一步的过滤/映射操作(省略)。所以基本上if false
条件没有任何影响,基本上被编译器删除。