我们正在使用Scala for
表达式并迭代具有多个循环的元素,例如 First Expression 代码:
for {
a <- 1 to 10
_ = print(a)
b <- 11 to 20
_ = print(b)
} yield 1
根据scala,for
表达式包含a <- 1 to 10
之类的表达式,如果存在多个表达式,则将它们视为内部循环。我们假设像上面的代码一样是第二个表达式:
for(a <- 1 to 10) {
print(a + " --- ")
for(b <- 11 to 20) {
print(b + " === ")
}
}
但两个代码的输出都不同。我们对表达式代码的预期输出如下:
1 --- 11 === 12 === 13 === 14 === 15 === 16 === 17 === 18 === 19 === 20 === 2 --- 11 === 12 === 13 === 14 === 15 === 16 === 17 === 18 === 19 === 20 === 3 --- 11 === 12 === 13 === 14 === 15 === 16 === 17 === 18 === 19 === 20 === 4 --- 11 === 12 === 13 === 14 === 15 === 16 === 17 === 18 === 19 === 20 === 5 --- 11 === 12 === 13 === 14 === 15 === 16 === 17 === 18 === 19 === 20 === 6 --- 11 === 12 === 13 === 14 === 15 === 16 === 17 === 18 === 19 === 20 === 7 --- 11 === 12 === 13 === 14 === 15 === 16 === 17 === 18 === 19 === 20 === 8 --- 11 === 12 === 13 === 14 === 15 === 16 === 17 === 18 === 19 === 20 === 9 --- 11 === 12 === 13 === 14 === 15 === 16 === 17 === 18 === 19 === 20 === 10 --- 11 === 12 === 13 === 14 === 15 === 16 === 17 === 18 === 19 === 20 ===
但此输出仅由第二 for
表达式生成。第一个表达式给了我们意想不到的输出。
1 --- 2 --- 3 --- 4 --- 5 --- 6 --- 7 --- 8 --- 9 --- 10 --- 11 === 12 === 13 === 14 === 15 === 16 === 17 === 18 === 19 === 20 === 11 === 12 === 13 === 14 === 15 === 16 === 17 === 18 === 19 === 20 === 11 === 12 === 13 === 14 === 15 === 16 === 17 === 18 === 19 === 20 === 11 === 12 === 13 === 14 === 15 === 16 === 17 === 18 === 19 === 20 === 11 === 12 === 13 === 14 === 15 === 16 === 17 === 18 === 19 === 20 === 11 === 12 === 13 === 14 === 15 === 16 === 17 === 18 === 19 === 20 === 11 === 12 === 13 === 14 === 15 === 16 === 17 === 18 === 19 === 20 === 11 === 12 === 13 === 14 === 15 === 16 === 17 === 18 === 19 === 20 === 11 === 12 === 13 === 14 === 15 === 16 === 17 === 18 === 19 === 20 === 11 === 12 === 13 === 14 === 15 === 16 === 17 === 18 === 19 === 20 ===
此输出打印第一个1 2 .. 10
元素,然后打印11 12 ... 20
个元素10次。为什么这两个输出不同?
第二次,同时使用其他for
表达式:
for {
a <- 1 to 10
_ = print(a + " --- ")
b <- 11 to 20
_ = print(b + " === " + a + " ")
} yield 1
输出结果为:
1 --- 2 --- 3 --- 4 --- 5 --- 6 --- 7 --- 8 --- 9 --- 10 --- 11 === 1 12 === 1 13 === 1 14 === 1 15 === 1 16 === 1 17 === 1 18 === 1 19 === 1 20 === 1 11 === 2 12 === 2 13 === 2 14 === 2 15 === 2 16 === 2 17 === 2 18 === 2 19 === 2 20 === 2 11 === 3 12 === 3 13 === 3 14 === 3 15 === 3 16 === 3 17 === 3 18 === 3 19 === 3 20 === 3 11 === 4 12 === 4 13 === 4 14 === 4 15 === 4 16 === 4 17 === 4 18 === 4 19 === 4 20 === 4 11 === 5 12 === 5 13 === 5 14 === 5 15 === 5 16 === 5 17 === 5 18 === 5 19 === 5 20 === 5 11 === 6 12 === 6 13 === 6 14 === 6 15 === 6 16 === 6 17 === 6 18 === 6 19 === 6 20 === 6 11 === 7 12 === 7 13 === 7 14 === 7 15 === 7 16 === 7 17 === 7 18 === 7 19 === 7 20 === 7 11 === 8 12 === 8 13 === 8 14 === 8 15 === 8 16 === 8 17 === 8 18 === 8 19 === 8 20 === 8 11 === 9 12 === 9 13 === 9 14 === 9 15 === 9 16 === 9 17 === 9 18 === 9 19 === 9 20 === 9 11 === 10 12 === 10 13 === 10 14 === 10 15 === 10 16 === 10 17 === 10 18 === 10 19 === 10 20 === 10
内部循环中存在a
的预期值。关于这种行为,我们仍然感到困惑。这种行为有什么好处?
答案 0 :(得分:1)
原因是:
for {
a <- 1 to 10
_ = print(a)
b <- 11 to 20
_ = print(b)
} yield 1
相当于:
(1 to 10).
map{a => print(a); a}.
flatMap{a =>
(11 to 20).
map{b => print(b); 1}
}
您看,由于1 to 10
是一个集合,因此映射它将立即构建新集合并执行所有print
语句。
如果您希望仅在需要时完成print(a)
,则可以将1 to 10
更改为(1 to 10).view
。
你的第二个循环相当于:
(1 to 10).foreach { a =>
print(a + " --- ")
(11 to 20).foreach { b =>
print(b + " === ")
}
}
自我解释了为什么它会在a
b
你的第二个问题相当于:
(1 to 10).map{a => print(a + " --- "); a}.
flatMap{ a =>
(11 to 20).map{ b =>
print(b + " === " + a + " ")
1
}
}
希望输出现在有意义。
答案 1 :(得分:0)
您应该阅读:http://docs.scala-lang.org/tutorials/FAQ/yield.html,并且:http://www.scala-lang.org/files/archive/spec/2.11/06-expressions.html#for-comprehensions-and-for-loops。
简而言之,您的第一个和第二个表达式实际上是非常不同的,因为一个使用yield
,而另一个则不使用yield
。具有map
的那个被称为“for-comprehension”,另一个被称为“for-loop”。
For-comprehension表示为flatMap
和foreach
的组合(加上一些与当前讨论无关的其他结构),for-loop表示为scala.Predef
.intWrapper(1)
.to(10)
.map[(Int, Unit), scala.collection.immutable.IndexedSeq[(Int, Unit)]]((a: Int) => {
val x$1 = scala.Predef.print(a.+(" --- "));
scala.Tuple2.apply[Int, Unit](a, x$1)
})(scala.collection.immutable.IndexedSeq.canBuildFrom[(Int, Unit)])
.flatMap[Int, Any]((x$4: (Int, Unit)) =>
(x$4: @scala.unchecked) match {
case scala.Tuple2((a @ _), _) =>
scala.Predef
.intWrapper(11)
.to(20)
.map[(Int, Unit), scala.collection.immutable.IndexedSeq[(Int, Unit)]]((b: Int) => {
val x$2 = scala.Predef.print(b.+(" === "));
scala.Tuple2.apply[Int, Unit](b, x$2)
})(scala.collection.immutable.IndexedSeq.canBuildFrom[(Int, Unit)])
.map[Int, scala.collection.immutable.IndexedSeq[Int]]((x$3: (Int, Unit)) =>
(x$3: @scala.unchecked) match {
case scala.Tuple2((b @ _), _) => 1
})(scala.collection.immutable.IndexedSeq.canBuildFrom[Int])
})(scala.collection.immutable.IndexedSeq.canBuildFrom[Int])
。
也就是说,您的第一个表达式由编译器转换为:
(1 to 10).map { a =>
val x = print(a + " --- ")
(a, x)
}.flatMap { case (_, _) =>
(11 to 20).map { b =>
val x = print(b + " === ")
(b, x)
}.map { _ =>
1
}
}
可以简化为:
(1 to 10).foreach(a => {
print(a + " --- ")
(11 to 20).foreach(b => print(b + " === "))
})
虽然第二个表达成:
map
这里很容易看出表达式之间的区别。无论如何,我会仔细阅读解释,特别是对于第一个表达,因为它是最复杂的。
在第一个表达式中,首先执行此(1 to 10).map { a =>
val x = print(a + " --- ")
(a, x)
}
:
1 --- 2 --- 3 --- 4 --- 5 --- 6 --- 7 --- 8 --- 9 --- 10 ---
按预期打印Tuple2[Int, Unit]
。现在,该表达式的结果是具有以下元素的集合:(1, ()), (2, ()), ... (10, ())
,结果集合中包含10个:flatMap
。
flatMap
在该中间集合上执行。因此11 === 12 === 13 === 14 === 15 === 16 === 17 === 18 === 19 === 20 ===
下的函数执行10次,并打印10次以下:1 --- 2 --- 3 --- 4 --- 5 --- 6 --- 7 --- 8 --- 9 --- 10 --- 11 === 12 === 13 === 14 === 15 === 16 === 17 === 18 === 19 === 20 === 11 === 12 === 13 === 14 === 15 === 16 === 17 === 18 === 19 === 20 === 11 === 12 === 13 === 14 === 15 === 16 === 17 === 18 === 19 === 20 === 11 === 12 === 13 === 14 === 15 === 16 === 17 === 18 === 19 === 20 === 11 === 12 === 13 === 14 === 15 === 16 === 17 === 18 === 19 === 20 === 11 === 12 === 13 === 14 === 15 === 16 === 17 === 18 === 19 === 20 === 11 === 12 === 13 === 14 === 15 === 16 === 17 === 18 === 19 === 20 === 11 === 12 === 13 === 14 === 15 === 16 === 17 === 18 === 19 === 20 === 11 === 12 === 13 === 14 === 15 === 16 === 17 === 18 === 19 === 20 === 11 === 12 === 13 === 14 === 15 === 16 === 17 === 18 === 19 === 20 ===
。
最终结果与问题相同,即第一个表达式打印:
mpldatacursor
我不会通过第二个表达式,因为它更简单,并且表示OP所期望的行为。
答案 2 :(得分:-1)
所以,
for {
a <- 1 to 10
_ = print("something")
b <- 11 to 20
_ = print("something else")
} yield 1
相当于:
(1 to 10).flatMap { a =>
print("something")
(11 to 20).map { b =>
print("something else")
1
}
}
现在,此循环的行为将取决于您用于第一个flatMap
的集合类型。 scala中的一些集合是&#34;懒惰&#34;其他集合不是。 &#34;非懒惰&#34;那些是那些,当你做foo.flatMap { x => ... }
时会立即评估整个集合的函数,并返回一个包含结果的新集合。惰性集合是一个,它将在访问元素时逐个评估转换。
Seq(1,2,3,4)
.map { n => println("foo" + n); n + 1 }
.map { n => println("bar" + n); n - 1 }
打印:
foo1
foo2
foo3
foo4
bar2
bar3
bar4
bar5
所有元素首先经过第一张地图,然后经过下一张地图。
另一方面:
Iterator(1,2,3,4)
.map { n => println("foo" + n); n + 1 }
.map { n => println("bar" + n); n - 1 }
根本不打印任何东西。
如果您在结果.next
上致电Iterator
,则会看到
foo1
bar2
因此,在转到第二个元素之前,它会通过两个映射发送第一个元素。
您的循环也会发生同样的事情:(1 to 10)
是Range
,这是一个渴望&#34;采集。这意味着,在继续for
之前,1
理解的整个主体都会针对2
进行评估。
您可以将行为切换到其他代码段,方法是使收集&#34;懒惰&#34;:a <- (1 to 10).iterator
或a <- (1 to 10).toStream
但是现在,for
理解的结果也将是懒惰的 - 它与列表中的第一个集合的类型相同,并且,为了看到整个事物被打印出来,你&#39必须&#34;强迫&#34; al元素的评估,例如,通过在结果上调用.toList
。