我是Scala的新手,我正在通过Cay Horstmann的 Scala for the Impatient 工作。它一直顺利,直到我理解,并且反对这个稍微神秘的段落(第2.6节,高级循环和理解):
[开始引用]
生成的集合与第一个生成器兼容。
for (c <- "Hello"; i <- 0 to 1) yield (c + i).toChar
// Yields "HIeflmlmop"
for (i <- 0 to 1; c <- "Hello") yield (c + i).toChar
// Yields Vector('H', 'e', 'l', 'l', 'o', 'I', 'f', 'm', 'm', 'p')
[结束语]
确实,在REPL中运行它,我看到第一个代码片段的结果是String类型,第二个代码片段的类型为scala.collection.immutable.IndexedSeq [Char]。
为什么类型不同?我想我理解了第二行代码,但我不明白为什么第一行也没有类型scala.collection.immutable.IndexedSeq [Char]。获得String而不是Vector会发生什么魔法?作者的意思是&#34;与第一个生成器&#34;?
兼容答案 0 :(得分:4)
如果可能,flatMap
和map
都试图构建相同类型的对象。第一个例子是有效的:
"Hello".flatMap { c => (0 to 1).map { i => (c + i).toChar } }
因为您正在调用String#flatMap
(确切地说StringOps#flatMap
),它会尝试构建String
,这是可能的,因为内部集合会返回Char
s的集合(尝试删除toChar
,你会看到一些非常不同的东西。)
在第二个例子中:
(0 to 1).flatMap { i => "Hello".map { c => yield (c + i).toChar }}
无法生成有效的Range
,因此Range#flatMap
会回退到Vector
。
另一个有趣的例子:
Map(1 -> 2, 3 -> 4).map(_._1) //> List(1, 3)
通常Map#map
会尝试生成Map
,但由于我们没有配对,所以它不可能,所以它会回归到List
<强>更新强>
如果您想要生成与默认值不同的内容(例如,我想生成Char
的列表),您甚至可以使用此技巧:
for {
_ <- List(None) // List type doesn't matter
c <- "Hello"
i <- 0 to 1
} yield (c + i).toChar //> List(H, I, e, f, l, m, l, m, o, p)
答案 1 :(得分:2)
要明白原因:
$ scalam -Xprint:typer
Welcome to Scala 2.12.0-M5 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_92).
Type in expressions for evaluation. Or try :help.
scala> for (c <- "Hello"; i <- 0 to 1) yield (c + i).toChar
[[syntax trees at end of typer]] // <console>
val res0: String = scala.Predef.augmentString("Hello").flatMap[Char, String](((c: Char) => scala.Predef.intWrapper(0).to(1).map[Char, scala.collection.immutable.IndexedSeq[Char]](((i: Int) => c.+(i).toChar))(immutable.this.IndexedSeq.canBuildFrom[Char])))(scala.Predef.StringCanBuildFrom);
当然,StringCanBuildFrom
会构建字符串。
这个问题有很多重复,因为每个人都很神秘。
答案 2 :(得分:0)
您用于第一个生成器的集合是用于从表达式转换为操作的类,如Scala Documentation中所述:
Scala的“for comprehensions”是使用foreach,map,flatMap,filter或withFilter组合多个操作的语法糖。 Scala实际上将for-expression转换为对这些方法的调用,因此任何提供它们的类或其子集都可以用于理解。