Scala中的生成器顺序"用于理解"影响答案

时间:2016-09-12 22:34:31

标签: scala

我是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;?

兼容

3 个答案:

答案 0 :(得分:4)

如果可能,flatMapmap都试图构建相同类型的对象。第一个例子是有效的:

"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转换为对这些方法的调用,因此任何提供它们的类或其子集都可以用于理解。