需要帮助找出Scala编译器错误
-
03-10-2019 - |
题
我一直在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)))
给出此错误:
"类型不匹配;
找到:(点,点)=> int
必要:(任何,点)=>"
我对这两个错误消息感到非常困惑。如果有人能更清楚地解释我在做什么,我真的很感激。是的,我看到第二个错误说我需要类型“任何”,但我不明白如何实现可以根据需要起作用的更改。显然,简单地将“ A:Point”更改为“ A:任何”不是一个可行的解决方案,那么我缺少什么?
解决方案
类型 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
, ,正如错误消息所说。
其他提示
这是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
.
另一个选择是 poly.points.foldLeft(Int.MaxValue)((b, a) => Math.min(b, a.x))
, ,它也应该与Scala 2.7.x.与还原版本相比,差异是
- 您有一个起始值(
Int.MaxValue
在我们的情况下,任何实际数据都会较小或等于此) - 元素类型和结果的类型之间没有任何约束,例如Readuceleft的下限约束,Eastsun的解决方案更加优雅。
顺便说一句,如果您已经有个案例类,则可以使用新的关键字,并且可以在伴随对象中使用自动生成的工厂方法。因此,创建Poly的行变为 val poly = Polygon(Point(2,5), Point(7,0), Point(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: ?}