在 Scala 中,为什么在使用toSet
中的TraversableOnce
功能时会出现以下情况?
如果使用以下代码创建工作表(在IntelliJ中),您将获得以下输出(注意:使用Scala 2.10.2):
val maps = List(List(1,2),List(3,4),List(5,6,7),List(8),List())
maps.flatMap( _.map( _ + " " ) )
maps.flatMap( _.map( _ + " " ) ).toSet
maps.flatMap( _.map( _ + " " ) ).toSet()
即。 res4生成一个布尔值
> maps: List[List[Int]] = List(List(1, 2), List(3, 4), List(5, 6, 7), List(8), List())
> res2: List[String] = List("1 ", "2 ", "3 ", "4 ", "5 ", "6 ", "7 ", "8 ")
> res3: scala.collection.immutable.Set[String] = Set("3 ", "8 ", "4 ", "5 ", "1 ", "6 ", "2 ", "7 ")
> res4: Boolean = false
毋庸置疑,我已经困惑了很久,直到我注意到toSet
在实现中没有使用括号,但为什么布尔值?
答案 0 :(得分:14)
正如您和其他人已经注意到的那样,toSet
没有提供参数列表。因此,使用括号调用它将始终导致编译错误,除非编译器找到一个需要参数的apply方法,因为在您的示例中就是这种情况:
scala> List(1).toSet()
res2: Boolean = false
scala> List(1).toSet.apply()
res3: Boolean = false
scalac有一个名为“调整参数列表”的功能,可以使用-Xlint
显示:
scala> List(1).toSet()
<console>:8: warning: Adapting argument list by inserting (): this is unlikely to be what you want.
signature: GenSetLike.apply(elem: A): Boolean
given arguments: <none>
after adaptation: GenSetLike((): Unit)
List(1).toSet()
^
res7: Boolean = false
scalac尝试将参数包装到元组中,正如人们可以在sources中看到的那样(其中空参数列表将被gen.mkTuple
视为Unit
文字):< / p>
/* Try packing all arguments into a Tuple and apply `fun`
* to that. This is the last thing which is tried (after
* default arguments)
*/
def tryTupleApply: Tree = (
if (eligibleForTupleConversion(paramTypes, argslen) && !phase.erasedTypes) {
val tupleArgs = List(atPos(tree.pos.makeTransparent)(gen.mkTuple(args)))
// expected one argument, but got 0 or >1 ==> try applying to tuple
// the inner "doTypedApply" does "extractUndetparams" => restore when it fails
val savedUndetparams = context.undetparams
silent(_.doTypedApply(tree, fun, tupleArgs, mode, pt)) map { t =>
// Depending on user options, may warn or error here if
// a Unit or tuple was inserted.
val keepTree = (
!mode.typingExprNotFun
|| t.symbol == null
|| checkValidAdaptation(t, args)
)
if (keepTree) t else EmptyTree
} orElse { _ => context.undetparams = savedUndetparams ; EmptyTree }
}
else EmptyTree
)
btw是一个not mentioned in the spec的功能。明确添加括号将使警告消失:
scala> List(1).toSet(())
res8: Boolean = false
现在剩下的一个问题是,为什么上面的代码不会产生编译错误,因为列表的类型为List[Int]
,而Set
的apply方法具有类型签名{{1}因此,在我们的案例中需要apply(A): Boolean
。原因是very well known problem和Int
的类型签名toSet
的结果。类型签名表示下限,这意味着任何超类型toSet[B >: A]: Set[B]
都可以作为参数传递。
因为在我们的情况下Int
被指定为参数的类型,所以编译器必须搜索与{{1}的类型签名匹配的Unit
和Unit
的公共超类型。 1}}。并且因为存在这样的类型,即Int
,编译器将推断该类型并继续前进而不会因错误而崩溃:
toSet