Scala TraversableOnce和toSet

时间:2013-11-26 18:09:30

标签: scala

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在实现中没有使用括号,但为什么布尔值?

1 个答案:

答案 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 problemInt的类型签名toSet的结果。类型签名表示下限,这意味着任何超类型toSet[B >: A]: Set[B]都可以作为参数传递。

因为在我们的情况下Int被指定为参数的类型,所以编译器必须搜索与{{1}的类型签名匹配的UnitUnit的公共超类型。 1}}。并且因为存在这样的类型,即Int,编译器将推断该类型并继续前进而不会因错误而崩溃:

toSet