加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 综合聚焦 > 服务器 > 安全 > 正文

任何尺寸的Scala矢量使用无形

发布时间:2020-12-16 21:32:48 所属栏目:安全 来源:网络整理
导读:我需要在n维欧几里德空间中测量距离,所以我必须创建多维向量,并且能够比较它们的尺寸并执行一些基本操作,如“或” – “.所以,我以为我会使用类型类型,如下所示: Implementing a generic Vector in Scala 但是在投入大量时间之后,我仍然不明白如何实现这一
我需要在n维欧几里德空间中测量距离,所以我必须创建多维向量,并且能够比较它们的尺寸并执行一些基本操作,如“或” – “.所以,我以为我会使用类型类型,如下所示:

Implementing a generic Vector in Scala

但是在投入大量时间之后,我仍然不明白如何实现这一点.我的意思是,我可以理解类型类后面的想法,可以使用它们,但不知道如何对它们应用无形的.提前感谢任何帮助,至少最简单的例子显示如何使用类型类与无形.

解决方法

第一步是要找到或定义一个类型类与您要支持的操作. scala.math.Numeric是一种可能性 – 它提供加法,减法等,但事实上,它也需要转换来自例如. Int意味着这可能不是正确的选择.像 algebra和 Spire这样的项目包括更合适的候选人.

定义类型类

为了保持简单,我们可以自己定义:

trait VectorLike[A] {
  def dim: Int
  def add(x: A,y: A): A
  def subtract(x: A,y: A): A
}

(请注意,我使用Like后缀来避免与集合库的Vector相冲突,这是一个有时候会看到的命名约定,但在这个上下文中并不需要在Scala中使用类型类更抽象的数学名称,如Monoid或Group更常见.)

定义实例

接下来,我们可以为二进制二维向量定义一个类型类实例,例如:

implicit val doubleVector2D: VectorLike[(Double,Double)] =
  new VectorLike[(Double,Double)] {
    def dim: Int = 2
    def add(x: (Double,Double),y: (Double,Double)): (Double,Double) =
      (x._1 + y._1,x._2 + y._2)
    def subtract(x: (Double,Double) =
      (x._1 - y._1,x._2 - y._2)
  }

现在我们可以这样使用这个实例:

scala> implicitly[VectorLike[(Double,Double)]].add((0.0,0.0),(1.0,1.0))
res0: (Double,Double) = (1.0,1.0)

这是很冗长的,但它是有效的.

隐性操作类

定义一个隐式的“ops”类通常很方便,使其看起来像类型为类型的值具有从类型类的操作派生的方法:

implicit class VectorLikeOps[A: VectorLike](wrapped: A) {
  def dim: Int = implicitly[VectorLike[A]].dim
  def |+|(other: A): A = implicitly[VectorLike[A]].add(wrapped,other)
  def |-|(other: A): A = implicitly[VectorLike[A]].subtract(wrapped,other)
}

现在你可以写下列内容:

scala> (0.0,0.0) |-| (1.0,1.0)
res1: (Double,Double) = (-1.0,-1.0)

scala> (0.0,0.0) |+| (1.0,1.0)
res2: (Double,1.0)

这不是必需的 – 它只是一个方便的模式,你会经常看到.

一般实例

当我们的doubleVector2D实例工作时,为每个数字类型定义这些实例是烦人的和模板的.我们可以通过使用scala.math.Numeric为任何二维数字向量向量提供实例来改善情况:

implicit def numericVector2D[A](implicit A: Numeric[A]): VectorLike[(A,A)] =
  new VectorLike[(A,A)] {
    def dim: Int = 2
    def add(x: (A,A),y: (A,A)): (A,A) = 
      (A.plus(x._1,y._1),A.plus(x._2,y._2))
    def subtract(x: (A,A) = 
      (A.minus(x._1,A.minus(x._2,y._2))
  }

请注意,我已经给数值类型类实例与通用类型(A)相同的名称.这是一种常见的惯例,对于类型需要单一类型类实例的方法,但并不是必需的 – 我们可以将其称为numericA或任何我们想要的.

现在我们可以使用数字实例的任何类型的元组使用我们的运算符:

scala> (1,2) |+| (3,4)
res3: (Int,Int) = (4,6)

这是一个很大的改进,但它仍然只是一个单一的矢量大小.

派生Shapeless的实例

我们还没有看到任何Shapeless,但是现在我们想要抽象出元组,这正是我们需要的.我们可以重写我们的通用实例来处理数字类型的任意元组.有很多方法我们可以写这个,但以下是我如何开始:

import shapeless._

trait HListVectorLike[L <: HList] extends VectorLike[L] { type El }

object HListVectorLike {
  type Aux[L <: HList,A] = HListVectorLike[L] { type El = A }

  implicit def vectorLikeHNil[A]: Aux[HNil,A] =
    new HListVectorLike[HNil] {
      type El = A
      def dim: Int = 0
      def add(x: HNil,y: HNil): HNil = HNil
      def subtract(x: HNil,y: HNil): HNil = HNil
    }

  implicit def vectorLikeHCons[T <: HList,A](implicit
    numeric: Numeric[A],instT: Aux[T,A]
  ): Aux[A :: T,A] = new HListVectorLike[A :: T] {
    type El = A
    def dim: Int = instT.dim + 1
    def add(x: A :: T,y: A :: T): A :: T =
      numeric.plus(x.head,y.head) :: instT.add(x.tail,y.tail)
    def subtract(x: A :: T,y: A :: T): A :: T =
      numeric.minus(x.head,y.head) :: instT.subtract(x.tail,y.tail)
  }
}

implicit def numericVector[P,Repr <: HList](implicit
  gen: Generic.Aux[P,Repr],inst: HListVectorLike[Repr]
): VectorLike[P] = new VectorLike[P] {
  def dim: Int = inst.dim
  def add(x: P,y: P): P = gen.from(inst.add(gen.to(x),gen.to(y)))
  def subtract(x: P,y: P): P = gen.from(inst.subtract(gen.to(x),gen.to(y)))
}

这看起来很复杂,但它是基本的模式,你会看到任何时候你使用Shapeless进行通用派生.我不会在这里详细描述发生了什么,但是请参见我的博客文章here讨论了一个类似的例子.

现在我们可以这样写:

scala> (1,2,3,4) |+| (5,6,7,8)
res1: (Int,Int,Int) = (6,8,10,12)

或这个:

scala> (0.0,0.0,2.0,3.0,4.0,5.0,6.0)
res4: (Double,Double,-2.0,-3.0,-4.0,-5.0,-6.0)

它只是工作.

其他向量表示

我一直在使用元组来表示上述所有示例中的多维向量,但是您也可以使用Shapeless的Sized,它是一个在类型级别编码其长度的均匀集合.您可以为Sized类型提供VectorLike实例,而不是(或除了)元组实例,而不对VectorLike本身进行任何更改.

你应该选择哪种代表取决于一些因素.元组很容易编写,对大多数Scala开发人员来说都是自然的,但如果您需要22维度的向量,那么它们就不会工作.

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读