4

现在,我花了一段时间才弄清楚为什么我的递归以某种方式设法破坏了堆栈。这是导致此问题的部分:

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:<

为什么?!

4

3 回答 3

7

我要冒昧地说出接受的答案可以说更多。

这是一个解析器错误。

守卫可以立即跟随生成器,但否则semi需要 a(实际的或推断的)。

这是语法。

在下文中,for 行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 生成器合并以生成新的 pairs 生成器(i,j)。然后守卫看起来就像它只是跟随(合成)生成器。

但是语法仍然是错误的。语法是我们的​​朋友!早在类型系统之前,它就是我们的 BFF。

在 for 的行上res5,很明显守卫不守卫 val def。

更新:

实现错误已降级(或升级,取决于您的观点)为规范错误

检查这种用法,如果控制它之前的 valdef,则一个守卫看起来像一个尾随,就像在 Perl 中一样,属于你最喜欢的样式检查器的权限范围。

于 2013-10-26T20:12:15.010 回答
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 语句视为一种过滤器;本教程有一些很好的例子。

考虑它的一种方法是命令式地遍历 for 循环,当您到达 if 语句时,如果该语句的计算结果为 false,您将继续循环的下一次迭代。

于 2013-10-25T22:09:07.113 回答
2

当我有这样的问题时,我会寻求查看反汇编代码的样子(例如,将 .class 文件提供给 JD-GUI)。

这个便于理解的反汇编代码的开头如下所示:

((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参数中的整数数组映射到一个AbstractFunction1()apply方法首先执行printlnnomatter what 然后分配Unit给参数j最后返回一个二元组(i,j)以进一步将其通过管道传递给进一步的过滤器/映射操作(省略)。所以基本上这个if false条件没有任何影响,基本上被编译器删除了。

于 2013-10-25T22:24:52.637 回答