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

Aar*_*ken 4 coding-style scala

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

val doubleEven = for (i <- 1 to 10; if i % 2 == 0)
  yield i * 2
Run Code Online (Sandbox Code Playgroud)

现在,将这种特殊语法内置到for循环而不是历史悠久的内容中,有什么额外的好处

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

风格如果?

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

mic*_*ebe 7

文件内容A.scala:

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

产量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();
      ()
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,编译器会创建一个Range从1到10.然后它调用withFilteron Range来过滤所有偶数.最后一步是调用map-method,乘以2.

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

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

val doubleEven = 1 to 10 filter(_%2==0) map(_*2)
Run Code Online (Sandbox Code Playgroud)

  • 其他一些:`-Xprint:all`,`-Xshow-phases`并尝试`-X`来查看更多选项和`-Y`. (2认同)

小智 7

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

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

由编译器转换为:

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

filter来自if-Guard,并且map来自后表达yield关键字.想到这样的yield事情是错误的return- 它实际上只是一个告诉编译器使用map方法而不是foreach方法的关键字.该map函数不能忽略调用它的集合的任何元素,因此filter必须首先调用它才能仅对某些元素进行操作.


huy*_*hjl 6

您可以将for构造视为select/from/where查询:

select (i * 2) from (1 to 10) where (i % 2 == 0)
Run Code Online (Sandbox Code Playgroud)

for部分而不是部分中使用过滤谓词yield还允许您使用多个生成器和过滤器:

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

如果过滤器不是太复杂的话,你可以做很多没有很多嵌套的东西,有时这对于可读性很方便.