用于Scala中的理解评估的奇怪(?)

时间:2013-10-25 21:47:06

标签: scala recursion for-comprehension

现在,我花了一段时间来弄清楚为什么我的递归会以某种方式破坏堆栈。这就是造成这个问题的部分:

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)会发生什么。 :其中

为什么?!

3 个答案:

答案 0 :(得分:7)

我要走出困境,说接受的答案可以说更多。

这是一个解析器错误。

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

Here is the syntax.

在下文中,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条件没有任何影响,基本上被编译器删除。