6

我一直在 scala 中从事一个项目,但我收到了一些我不太理解的错误消息。我正在使用的类相对简单。例如:

abstract class Shape
case class Point(x: Int, y: Int) extends Shape
case class Polygon(points: Point*) extends Shape

现在假设我创建了一个多边形:

val poly = new Polygon(new Point(2,5), new Point(7,0), new Point(3,1))

然后,如果我试图确定可能包含多边形的最小矩形的位置和大小,我会得到各种我不太理解的错误。

以下是不同尝试的片段以及它们产生的相应错误消息。

val upperLeftX = poly.points.reduceLeft(Math.min(_.x, _.x))

给出错误:
缺少扩展函数的参数类型 ((x$1) => x$1.x)

val upperLeftX =  
         poly.points.reduceLeft((a: Point, b: Point) => (Math.min(a.x, b.x)))

给出此错误:
类型不匹配;
发现:(Point, Point) => Int
required: (Any, Point) => Any

我对这两个错误消息都很困惑。如果有人能更清楚地解释我做错了什么,我将不胜感激。是的,我看到第二个错误说我需要输入“Any”,但我不完全了解如何实现我需要的更改。显然,简单地将“a:Point”更改为“a:Any”不是一个可行的解决方案,那么我错过了什么?

4

4 回答 4

6

的类型reduceLeftreduceLeft[B >: A](op: (B, A) => B): BAPoint,并且您正在尝试将其应用于(a: Point, b: Point) => (Math.min(a.x, b.x))

编译器的原因是:Math.min(a.x, b.x)返回Int,所以Int必须是 的子类型B。并且B还必须是 的超类型Point。为什么?B是累加器的类型,它的初始值是Point你的第一个Polygon。这就是 的意思B >: A

Intand的唯一超类型PointAny; so BisAnyopshould be的类型(Any, Point) => Any,正如错误消息所说。

于 2010-05-20T07:13:07.467 回答
2

这是 Scala 2.8.0.RC2

scala> abstract class Shape
defined class Shape

scala> case class Point(x: Int, y: Int) extends Shape
defined class Point

scala> case class Polygon(points: Point*) extends Shape
defined class Polygon

scala> val poly = new Polygon(new Point(2,5), new Point(7,0), new Point(3,1))
poly: Polygon = Polygon(WrappedArray(Point(2,5), Point(7,0), Point(3,1)))

scala> val upperLeftX = poly.points.reduceLeft((a:Point,b:Point) => if (a.x < b.x) a else b)
upperLeftX: Point = Point(2,5)

reduceLeft这里需要一个类型的函数(Point, Point) => Point。(更准确地说(B, Point) => BB下限为Point。请参阅Scaladoc的方法reduceLeft

于 2010-05-20T06:10:23.553 回答
2

另一种选择是poly.points.foldLeft(Int.MaxValue)((b, a) => Math.min(b, a.x)),它也应该适用于 Scala 2.7.x。与 reduceLeft 版本相比的差异是

  • 你有一个起始值(Int.MaxValue在我们的例子中,任何真实数据都将小于或等于这个值)
  • 元素的类型和结果的类型之间没有约束,例如 reduceLeft 的下限约束。不过 Eastsun 的解决方案更加优雅。

顺便说一句,如果您已经有案例类,则可以省略 new 关键字,并且可以在伴随对象中使用自动生成的工厂方法。所以创建多边形的行变成了val poly = Polygon(Point(2,5), Point(7,0), Point(3,1)),这更容易阅读。

于 2010-05-20T07:18:34.350 回答
1

我看到每个人似乎都已经锁定了第二个片段,所以我将回答第一个:

val upperLeftX = poly.points.reduceLeft(Math.min(_.x, _.x))

你的意思是这样的:

val upperLeftX = poly.points.reduceLeft((a, b) => Math.min(a.x, b.x))

但是,这不是下划线的工作方式。有很多含义需要强调,但其中两个在这里是相关的。

首先,它可能意味着部分功能应用程序。例如,Math.min(_, 0)部分参数应用到min,并返回一个应用其余参数的函数。换句话说,它相当于x => Math.min(x, 0),忽略类型注释。无论如何,仅当下划线本身代替一个(或多个)参数时,此含义才适用。

但是,在您的示例中并非如此,因为您.x在下划线后添加了 a 。如果下划线出现在任何类型的表达式中,例如您的示例中的方法调用,则该下划线是匿名函数中参数的占位符。

在这第二个含义中,了解匿名函数的边界尤为重要。具体来说,匿名函数将由最里面的括号或括住它的大括号或任何逗号分隔。

现在,将该规则应用于第一个片段中的表达式意味着编译器可以看到 snipper,如下所示:

val upperLeftX = poly.points.reduceLeft(Math.min(a => a.x, b => b.x))

所以,这里有两个问题。首先,您将两个函数传递给min而不是两个双打。其次,由于min不期望接收函数,编译器无法推断这些函数的类型。由于您没有提供有关类型ab以上类型的任何信息,因此它对此有所抱怨。

如果您确实提供了此类类型,则错误消息将如下所示:

<console>:6: error: type mismatch;
 found   : Int
 required: ?{val x: ?}
于 2010-05-20T16:09:40.787 回答