Проверка во время компиляции для векторного измерения - PullRequest
1 голос
/ 12 февраля 2011

Я реализую некоторые легкие математические векторы в Scala.Я хотел бы использовать систему типов для проверки совместимости векторов во время компиляции.Например, попытка добавить вектор измерения 2 к другому вектору измерения 3 приведет к ошибке компиляции.

До сих пор я определял измерения как классы дел:

sealed trait Dim
case class One() extends Dim
case class Two() extends Dim
case class Three() extends Dim
case class Four() extends Dim
case class Five() extends Dim

Ивот определение векторов:

class Vec[D <: Dim](val values: Vector[Double]) {

  def apply(i: Int) = values(i)

  def *(k: Double) = new Vec[D]( values.map(_*k) )

  def +(that: Vec[D]) = {
    val newValues = ( values zip that.values ) map { 
      pair => pair._1 + pair._2
    }
    new Vec[D](newValues)
  }

  override lazy val toString = "Vec(" + values.mkString(", ") + ")"

}

Это решение хорошо работает, однако у меня есть две проблемы:

  • Как добавить метод dimension():Int, который возвращаетизмерение (т. е. 3 для Vec[Three])?

  • Как можно обрабатывать более высокие измерения без предварительного объявления всех необходимых классов дел?

PS: Я знаю, что есть хорошие математические векторные библиотеки, я просто пытаюсь улучшить свое понимание scala.

Ответы [ 3 ]

3 голосов
/ 12 февраля 2011

Мои предложения:

1 голос
/ 12 февраля 2011

Я предлагаю что-то вроде этого:

sealed abstract class Dim(val dimension:Int)

object Dim {
  class One extends Dim(1)
  class Two extends Dim(2)
  class Three extends Dim(3)

  implicit object One extends One
  implicit object Two extends Two
  implicit object Three extends Three
}

case class Vec[D <: Dim](values: Vector[Double])(implicit dim:D) {

  require(values.size == dim.dimension)

  def apply(i: Int) = values(i)

  def *(k: Double) = Vec[D]( values.map(_*k) )

  def +(that: Vec[D]) = Vec[D](
     ( values zip that.values ) map {
      pair => pair._1 + pair._2
  })

  override lazy val toString = values.mkString("Vec(",", ",")")
}

Конечно, вы можете получить только время выполнения проверки длины вектора таким образом, но, как уже отмечали другие, вам нужно что-то вроде церковных чисел или других методов программирования на уровне типов для выполнения проверок времени компиляции.

  import Dim._
  val a = Vec[Two](Vector(1.0,2.0))
  val b = Vec[Two](Vector(1.0,3.0))
  println(a + b)
  //--> Vec(2.0, 5.0) 

  val c = Vec[Three](Vector(1.0,3.0)) 
  //--> Exception in thread "main" java.lang.ExceptionInInitializerError
  //-->        at scalatest.vecTest.main(vecTest.scala)
  //--> Caused by: java.lang.IllegalArgumentException: requirement failed
0 голосов
/ 12 февраля 2011

Если вы не хотите идти по маршруту Пеано, вы всегда можете создать свой Vec с D, а затем использовать экземпляр для определения размера с помощью сопутствующего объекта Dim.Например:

object Dim {
  def dimensionOf(d : Dim) = d match {
    case One => 1
    case Two => 2
    case Three => 3
  }
}
sealed trait Dim

Я думаю, что по вашему выбору вы должны использовать объекты case, а не классы case:

case object One extends Dim
case object Two extends Dim

Тогда в вашем векторе вам, возможно, придется хранить Dim.:

object Vec {
  def vec1 = new Vec[One](One)
  def vec2 = new Vec[Two](Two)
  def vec3 = new Vec[Three](Three)
}

class Vec[D <: Dim](d : D) {
  def dimension : Int = Dim dimensionOf d
  //etc
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...