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
必須:(任意、ポイント)=>"
これらのエラーメッセージの両方について非常に混乱しています。誰かが私が誤ってやっていることをより明確に説明できれば、本当に感謝しています。はい、2番目のエラーでは「任意」タイプが必要だと書かれていることがわかりますが、必要に応じて機能する変更を実装する方法を正確に理解していません。明らかに、単に「a:ポイント」を「a:any」に変更することは実行可能な解決策ではないので、何が欠けていますか?
解決
タイプの 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でも動作するはずです。 reduceleftバージョンと比較した違いはそうです
- 開始値があります(
Int.MaxValue
私たちの場合、実際のデータはこれと等しくなります) - 要素のタイプと結果のタイプの間には制約はありません。それにもかかわらず、reduceleftの下限の制約はさらにエレガントです。
ところで、既にケースクラスがある場合は、新しいキーワードを並べることができ、コンパニオンオブジェクトで自動的に生成された工場メソッドを使用できます。したがって、ポリを作成するラインはなります val poly = Polygon(Point(2,5), Point(7,0), Point(3,1))
, 、読みやすいです。
誰もが2番目のスニペットにラッチしているように見えるので、最初のスニペットに答えます。
val upperLeftX = poly.points.reduceLeft(Math.min(_.x, _.x))
あなたはそれを意味することを意図しました:
val upperLeftX = poly.points.reduceLeft((a, b) => Math.min(a.x, b.x))
ただし、それはアンダースコアの仕組みではありません。強調する多くの意味がありますが、そのうちの2つはここで関連しています。
まず、部分関数アプリケーションを意味する場合があります。例えば、 Math.min(_, 0)
します 部分的に適用します のパラメーター min
, 、および残りのものを適用する関数を返します。言い換えれば、それは同等です x => Math.min(x, 0)
, 、タイプの注釈を無視します。とにかく、この意味 それだけ アンダースコアがパラメーターの1つ(またはそれ以上)の代わりにすべて単独である場合に適用されます。
ただし、あなたの例ではそうではありません。 .x
アンダースコアの後。アンダースコアが例のメソッド呼び出しなど、あらゆる種類の式で表示される場合、そのアンダースコアは匿名関数のパラメーターのプレースホルダーです。
この2番目の意味では、匿名関数の境界を理解することが特に重要です。具体的には、匿名関数は、それを囲む最も内側の括弧または巻き毛ブラケットによって区切られます。
さて、最初のスニペットでそのルールを式に適用することは、スニッパーがこのようなコンパイラに見られることを意味します。
val upperLeftX = poly.points.reduceLeft(Math.min(a => a.x, b => b.x))
したがって、ここには2つの問題があります。まず、2つの関数を渡しています min
2つのダブルの代わりに。第二に、なぜなら min
関数を受信することを期待していないため、コンパイラはこれらの機能のタイプが何であるかを推測できません。あなたはタイプの情報を提供しなかったので a
と b
上記では、それについて不平を言っています。
そのようなタイプを提供した場合、エラーメッセージは次のようなものになります。
<console>:6: error: type mismatch;
found : Int
required: ?{val x: ?}