我一直在使用scala开发一个项目,但是我收到一些我不太了解的错误消息。我正在使用的课程相对简单。 例如:
abstract class Shape
case class Point(x: Int, y: Int) extends Shape
case class Polygon(points: Point*) extends Shape
现在假设我创建了一个Polygon:
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)))
给出此错误:
“类型不匹配;
发现:(点,点)=>诠释
required :( Any,Point)=>任何“
我对这两个错误消息感到非常困惑。如果有人能够更清楚地解释我做错了什么,我真的很感激。是的,我看到第二个错误说我需要输入“Any”但我不明白如何实现一个可以根据需要进行更改的更改。显然,简单地将“a:Point”改为“a:Any”并不是一个可行的解决方案,所以我错过了什么?
答案 0 :(得分:6)
reduceLeft
的类型为reduceLeft[B >: A](op: (B, A) => B): B
,A
为Point
,您尝试将其应用于(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
的含义。
Int
和Point
的唯一超类型是Any
;因此B
为Any
且op
的类型应为(Any, Point) => Any
,正如错误消息所示。
答案 1 :(得分: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) => B
B
的下限为Point
。请参阅方法reduceLeft
的{{3}}。
答案 2 :(得分:2)
另一种选择是poly.points.foldLeft(Int.MaxValue)((b, a) => Math.min(b, a.x))
,它也适用于Scala 2.7.x.与reduceLeft版本相比的差异是
Int.MaxValue
在我们的例子中,任何真实数据都会小于或等于此) BTW如果您已经有案例类,可以省略new关键字,并且可以在随播对象中使用自动生成的工厂方法。因此,创建poly的行变为val poly = Polygon(Point(2,5), Point(7,0), Point(3,1))
,这更容易阅读。
答案 3 :(得分: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
不期望接收函数,所以编译器无法推断这些函数的类型。由于您未提供有关上述a
和b
类型的任何信息,因此会对此抱怨。
如果你提供了这样的类型,错误信息将是这样的:
<console>:6: error: type mismatch;
found : Int
required: ?{val x: ?}