代码之家  ›  专栏  ›  技术社区  ›  Aaron Yodaiken

为什么在scala中修改了if位置?

  •  4
  • Aaron Yodaiken  · 技术社区  · 14 年前

    所以我在学习scala,遇到了一些这样的例子:

    val doubleEven = for (i <- 1 to 10; if i % 2 == 0)
      yield i * 2
    

    现在,将这种特殊的语法内置到for循环中,而不是久负盛名的for循环中,还有什么好处呢?

    val doubleEven = for(i <- 1 to 10){
      if(i % 2 ==  0)
        yield i*2
    }
    

    风格如果?

    编辑: 当然,后一个例子实际上不起作用。但是我很好奇为什么斯卡拉人决定使用一个单独的语法。

    3 回复  |  直到 14 年前
        1
  •  7
  •   michael.kebe    14 年前

    文件内容 A.scala :

    object A {
      val doubleEven1 = for (i <- 1 to 10; if i % 2 == 0) yield i * 2
    }
    

    产量 scalac -Xprint:jvm A.scala :

    [[syntax trees at end of jvm]]// Scala source: A.scala
    package <empty> {
      final class A extends java.lang.Object with ScalaObject {
        private[this] val doubleEven1: scala.collection.immutable.IndexedSeq = _;
        <stable> <accessor> def doubleEven1(): scala.collection.immutable.IndexedSeq = A.this.doubleEven1;
        def this(): object A = {
          A.super.this();
          A.this.doubleEven1 = scala.this.Predef.intWrapper(1).to(10).withFilter({
            (new A$$anonfun$1(): Function1)
          }).map({
            (new A$$anonfun$2(): Function1)
          }, immutable.this.IndexedSeq.canBuildFrom()).$asInstanceOf[scala.collection.immutable.IndexedSeq]();
          ()
        }
      };
      @SerialVersionUID(0) @serializable final <synthetic> class A$$anonfun$1 extends scala.runtime.AbstractFunction1$mcZI$sp {
        final def apply(i: Int): Boolean = A$$anonfun$1.this.apply$mcZI$sp(i);
        <specialized> def apply$mcZI$sp(v1: Int): Boolean = v1.%(2).==(0);
        final <bridge> def apply(v1: java.lang.Object): java.lang.Object = scala.Boolean.box(A$$anonfun$1.this.apply(scala.Int.unbox(v1)));
        def this(): A$$anonfun$1 = {
          A$$anonfun$1.super.this();
          ()
        }
      };
      @SerialVersionUID(0) @serializable final <synthetic> class A$$anonfun$2 extends scala.runtime.AbstractFunction1$mcII$sp {
        final def apply(i: Int): Int = A$$anonfun$2.this.apply$mcII$sp(i);
        <specialized> def apply$mcII$sp(v1: Int): Int = v1.*(2);
        final <bridge> def apply(v1: java.lang.Object): java.lang.Object = scala.Int.box(A$$anonfun$2.this.apply(scala.Int.unbox(v1)));
        def this(): A$$anonfun$2 = {
          A$$anonfun$2.super.this();
          ()
        }
      }
    }
    

    如您所见,编译器创建一个 Range 从1到10。然后它调用 withFilter 在那 范围 过滤所有偶数。最后一步是 map -方法与2相乘。

    你的第二个例子是否有效,语义会有点不同。因为它将在每次迭代中执行循环体而不进行过滤。

    这是一个品味问题,但我更喜欢这样:

    val doubleEven = 1 to 10 filter(_%2==0) map(_*2)
    
        2
  •  7
  •   user382157    14 年前

    最简单的解释方法是过滤 之前 对for循环的主体进行了评估,并且此语法旨在反映这一点。

    表达式 for (i <- 1 to 10; if i % 2 == 0) yield i * 2

    被编译器转换为如下类型:

    (1 to 10).filter(_ % 2 == 0).map(_ * 2)

    这个 filter 来自于 if -守卫,以及 map 来自后面的表达式 yield 关键字。想起来是错误的 产量 就像 return -它实际上只是一个关键字,告诉编译器使用 地图 方法而不是 foreach 方法。这个 地图 函数不能忽略调用它的集合的任何元素,因此 滤波器 必须首先调用,以便只对某些元素进行操作。

        3
  •  6
  •   huynhjl bhericher    14 年前

    你可以想到 for 构造为select/from/where查询:

    select (i * 2) from (1 to 10) where (i % 2 == 0)
    

    对于 而不是 yield 其中一个还允许您使用多个生成器和筛选器:

    for (i <- 1 to 10; if i % 2 == 0; j <- 1 to 10; if j % 2 == 1) yield { (i,j) } 
    

    你可以在没有很多嵌套的情况下做很多事情,如果过滤器不太复杂的话,有时候这对于可读性是很方便的。