Scala - 在编译时强制执行 Vector 的大小 [英] Scala - Enforcing size of Vector at compile time

查看:35
本文介绍了Scala - 在编译时强制执行 Vector 的大小的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

是否可以强制在编译时传递给方法的 Vector 的大小?我想使用空间中看起来像这样的点集合来建模一个 n 维欧几里得空间(这就是我现在所拥有的):

Is it possible to enforce the size of a Vector passed in to a method at compile time? I want to model an n-dimensional Euclidean space using a collection of points in the space that looks something like this (this is what I have now):

case class EuclideanPoint(coordinates: Vector[Double]) {
  def distanceTo(desination: EuclieanPoint): Double = ???
}

如果我有一个通过 EuclideanPoint(Vector(1, 0, 0)) 创建的坐标,它是一个 3D 欧几里得点.鉴于此,我想确保在对 distanceTo 的调用中传递的目标点具有相同的维度.

If I have a coordinate that is created via EuclideanPoint(Vector(1, 0, 0)), it is a 3D Euclidean point. Given that, I want to make sure the destination point passed in a call to distanceTo is of the same dimension.

我知道我可以通过使用 Tuple1Tuple22 来做到这一点,但我想表示许多不同的几何空间,如果我要为每个空间编写 22 个类用 Tuples 做到的 - 有没有更好的方法?

I know I can do this by using Tuple1 to Tuple22, but I want to represent many different geometric spaces and I would be writing 22 classes for each space if I did it with Tuples - is there a better way?

推荐答案

可以通过多种方式来做到这一点,这些方式或多或少都类似于 Randall Schulz 在评论中所描述的.Shapeless 库 提供了一个特别方便的实现,它可以让你得到非常接近你想要的东西:

It is possible to do this in a number of ways that all look more or less like what Randall Schulz has described in a comment. The Shapeless library provides a particularly convenient implementation, which lets you get something pretty close to what you want like this:

import shapeless._

case class EuclideanPoint[N <: Nat](
   coordinates: Sized[IndexedSeq[Double], N] { type A = Double }
) {
  def distanceTo(destination: EuclideanPoint[N]): Double = 
    math.sqrt(
      (this.coordinates zip destination.coordinates).map {
        case (a, b) => (a - b) * (a - b)
      }.sum
    )
}

现在您可以编写以下内容:

Now you can write the following:

val orig2d = EuclideanPoint(Sized(0.0, 0.0))
val unit2d = EuclideanPoint(Sized(1.0, 1.0))

val orig3d = EuclideanPoint(Sized(0.0, 0.0, 0.0))
val unit3d = EuclideanPoint(Sized(1.0, 1.0, 1.0))

还有:

scala> orig2d distanceTo unit2d
res0: Double = 1.4142135623730951

scala> orig3d distanceTo unit3d
res1: Double = 1.7320508075688772

但不是:

scala> orig2d distanceTo unit3d
<console>:15: error: type mismatch;
 found   : EuclideanPoint[shapeless.Nat._3]
 required: EuclideanPoint[shapeless.Nat._2]
              orig2d distanceTo unit3d
                                ^

Sized 带有许多不错的特性,包括一些带有关于长度的静态保证的集合操作.例如,我们可以编写以下内容:

Sized comes with a number of nice features, including a handful of collections operations that carry along static guarantees about length. We can write the following for example:

val somewhere = EuclideanPoint(Sized(0.0) ++ Sized(1.0, 0.0))

并且在三维空间中有一个普通的旧点.

And have an ordinary old point in three-dimensional space.

这篇关于Scala - 在编译时强制执行 Vector 的大小的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆