2010-05-20 37 views
6

我一直在scala项目中工作,但我收到了一些我不太明白的错误消息。我正在使用的课程相对简单。 例如:需要帮助搞清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”,但我不明白如何实施可以根据需要进行更改的更改。显然,简单地将“a:Point”改为“a:Any”不是一个可行的解决方案,所以我错过了什么?

回答

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是累加器的类型,并且其初始值是您的Polygon中的第一个Point。这就是B >: A的含义。

IntPoint的唯一超类型是Any;因此BAnyop的类型应为(Any, Point) => Any,就像错误消息所述。

+0

好的......我认为我其实已经明白这一点。感谢您的解释! – klactose 2010-05-20 07:24:55

2

这是斯卡拉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。在方法reduceLeftScaladoc

+0

这将返回一个点,但是在我的例子,我试图返回一个int(最小的x坐标为精确)。我相信我的问题在于某种程度上是由于编译器对于它应该返回的类型感到困惑。 更改您的代码返回一个Int(这是我需要的): val upperLeftX = poly.points.reduceLeft((a:Point,b:Point)=> if(ax klactose 2010-05-20 06:27:24

+0

只需试试这一个然后:'val upperLeftX = poly.points.reduceLeft((a,b)=> if(ax 2010-05-20 06:32:04

+1

for scala2.8: val upperLeftX = poly.points.map(_。x).min – Eastsun 2010-05-20 06:32:04

2

另一种替代方法是poly.points.foldLeft(Int.MaxValue)((b, a) => Math.min(b, a.x)),它也应该与Scala 2.7.x一起使用。相比reduceLeft版本的差异

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

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

+0

谢谢,我会记住关于新关键字的信息 – klactose 2010-05-20 07:27:04

+0

@Rahul:感谢您的更正。我很快找到了,但找不到它:-) – 2010-05-20 10:49:40

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。如果下划线出现在任何类型的表达式中(例如您的示例中的方法调用),则该下划线是匿名函数中参数的占位符。

在这第二层意思是尤为重要的是了解匿名函数的边界。具体来说,匿名函数将由最内括号或括号括起来的括号或任何逗号分隔。现在

,应用该规则的第一个片段的表达意味着剪断由编译器这样的看出:

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: ?}