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

scala – 在编译时添加两个相同大小的列表

发布时间:2020-12-16 18:41:53 所属栏目:安全 来源:网络整理
导读:参见英文答案 Scala – Enforcing size of Vector at compile time????????????????????????????????????2个 在Idris中,我可以通过以下方式添加两个相同大小的向量: module MatrixMathimport Data.VectaddHelper : (Num n) = Vect k n - Vect k n - Vect k
参见英文答案 > Scala – Enforcing size of Vector at compile time????????????????????????????????????2个
在Idris中,我可以通过以下方式添加两个相同大小的向量:

module MatrixMath

import Data.Vect

addHelper : (Num n) => Vect k n -> Vect k n -> Vect k n
addHelper = zipWith (+)

在REPL上编译后:

*MatrixMath> :l MatrixMath.idr 
Type checking ./MatrixMath.idr

然后我可以用两个大小为3的向量来调用它:

*MatrixMath> addHelper [1,2,3] [4,5,6]
[5,7,9] : Vect 3 Integer

但是,当我尝试在两个不同大小的向量上调用addHelper时,它将无法编译:

*MatrixMath> addHelper [1,3] [1]
(input):1:20:When checking argument xs to constructor Data.Vect.:::
        Type mismatch between
                Vect 0 a (Type of [])
        and
                Vect 2 n (Expected type)

        Specifically:
                Type mismatch between
                        0
                and
                        2

我怎么能在Scala中写这个?

解决方法

对于这种问题,shapeless通常是正确的问题.
Shapeless已经具有类型级数(shapless.Nat)和具有编译时已知大小(shapeless.Sized)的集合的抽象.

实现的第一个看法可能是这样的

import shapeless.{ Sized,Nat }
import shapeless.ops.nat.ToInt
import shapeless.syntax.sized._

def Vect[A](n: Nat)(xs: A*)(implicit toInt : ToInt[n.N]) =
  xs.toVector.sized(n).get

def add[A,N <: Nat](left: Sized[Vector[A],N],right: Sized[Vector[A],N])(implicit A: Numeric[A]) =
  Sized.wrap[Vector[A],N]((left,right).zipped.map(A.plus))

它的用法:

scala> add(Vect(3)(1,3),Vect(3)(4,6))
res0: shapeless.Sized[Vector[Int],shapeless.Succ[shapeless.Succ[shapeless.Succ[shapeless._0]]]] = Vector(5,9)

scala> add(Vect(3)(1,Vect(1)(1))
<console>:30: error: type mismatch;
  // long and misleading error message about variance
  // but at least it failed to compile

虽然这看起来很有效,但它是一个严重的缺点 – 您必须确保提供的长度和参数数量匹配,否则您将收到运行时错误.

scala> Vect(1)(1,3)
java.util.NoSuchElementException: None.get
  at scala.None$.get(Option.scala:347)
  at scala.None$.get(Option.scala:345)
  at .Vect(<console>:27)
  ... 33 elided

我们可以比这更好.您可以直接使用Sized而不是其他构造函数.
此外,如果我们使用两个参数列表定义add,我们可以获得更好的错误消息(它不像Idris提供的那样好,但它可以使用):

import shapeless.{ Sized,Nat }

def add[A,N <: Nat](left: Sized[IndexedSeq[A],N])(right: Sized[IndexedSeq[A],N])(implicit A: Numeric[A]) =
  Sized.wrap[IndexedSeq[A],right).zipped.map(A.plus))

// ...

add(Sized(1,3))(Sized(4,6))
res0: shapeless.Sized[IndexedSeq[Int],shapeless.nat._3] = Vector(5,9)

scala> add(Sized(1,3))(Sized(1))
<console>:24: error: polymorphic expression cannot be instantiated to expected type;
 found   : [CC[_]]shapeless.Sized[CC[Int],shapeless.nat._1]
    (which expands to)  [CC[_]]shapeless.Sized[CC[Int],shapeless.Succ[shapeless._0]]
 required: shapeless.Sized[IndexedSeq[Int],shapeless.nat._3]
    (which expands to)  shapeless.Sized[IndexedSeq[Int],shapeless.Succ[shapeless.Succ[shapeless.Succ[shapeless._0]]]]
       add(Sized(1,3))(Sized(1))

但我们可以走得更远. Shapeless还提供元组和大小之间的转换,因此我们可以写:

import shapeless.{ Sized,Nat }
import shapeless.ops.tuple.ToSized

def Vect[A,P <: Product](xs: P)(implicit toSized: ToSized[P,Vector]) =
  toSized(xs)

def add[A,right).zipped.map(A.plus))

这工作,大小是从提供的元组推断:

scala> add(Vect(1,Vect(4,9)

scala> add(Vect(1,3))(Vect(1))
<console>:27: error: type mismatch;
 found   : shapeless.Sized[scala.collection.immutable.Vector[Int],shapeless.Succ[shapeless.Succ[shapeless.Succ[shapeless.Succ[shapeless._0]]]]]
 required: shapeless.Sized[Vector[Int],shapeless.Succ[shapeless.Succ[shapeless.Succ[shapeless._0]]]]
       add(Vect(1,3))(Vect(4,6,7))

不幸的是,该示例中的语法仅适用于称为参数自适应的功能,其中scalac会自动将Vect中的多个参数转换为我们需要的元组.由于这个“功能”也可能导致非常讨厌的错误,我发现自己几乎总是使用-Yno-adapted-args禁用它.使用这个标志,我们必须自己明确地编写元组:

scala> Vect(1,3)
<console>:26: warning: No automatic adaptation here: use explicit parentheses.
        signature: Vect[A,P <: Product](xs: P)(implicit toSized: shapeless.ops.tuple.ToSized[P,Vector]): toSized.Out
  given arguments: 1,3
 after adaptation: Vect((1,3): (Int,Int,Int))
       Vect(1,3)
           ^
<console>:26: error: too many arguments for method Vect: (xs: (Int,Int))(implicit toSized: shapeless.ops.tuple.ToSized[(Int,Int),Vector])toSized.Out
       Vect(1,3)
           ^

scala> Vect((1,3))
res1: shapeless.Sized[scala.collection.immutable.Vector[Int],shapeless.Succ[shapeless.Succ[shapeless.Succ[shapeless._0]]]] = Vector(1,3)

scala> add(Vect((1,3)))(Vect((4,6)))
res2: shapeless.Sized[Vector[Int],9)

此外,我们只能使用长度达22,scala不支持更大的元组.

scala> Vect((1,3,4,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23))
<console>:26: error: object <none> is not a member of package scala
       Vect((1,23))

那么,我们可以获得更好的语法吗?事实证明,我们可以.无形可以为我们做包装,而不是使用HList:

import shapeless.ops.hlist.ToSized
import shapeless.{ ProductArgs,HList,Nat,Sized }

object Vect extends ProductArgs {
  def applyProduct[L <: HList](l: L)(implicit toSized: ToSized[L,Vector]) =
    toSized(l)
}

def add[A,N])(right: Sized[Vector[A],right).zipped.map(A.plus))

它有效:

scala> add(Vect(1,shapeless.Succ[shapeless._0]]
 required: shapeless.Sized[Vector[Int],3))(Vect(1))
                              ^

scala> Vect(1,23)
res2: shapeless.Sized[scala.collection.immutable.Vector[Int],shapeless.Succ[shapeless.Succ[... long type elided... ]]] = Vector(1,23)

你可以从那里走得更远,例如将Size包装在你自己的类中

import shapeless.ops.hlist.ToSized
import shapeless.{ ProductArgs,Vector]): Vect[toSized.Lub,toSized.N] =
    new Vect(toSized(l))
}

class Vect[A,N <: Nat] private (val self: Sized[Vector[A],N]) extends Proxy.Typed[Sized[Vector[A],N]] {
  def add(other: Vect[A,N])(implicit A: Numeric[A]): Vect[A,N] =
    new Vect(Sized.wrap[Vector[A],N]((self,other.self).zipped.map(A.plus)))
}

// ...

scala> Vect(1,3) add Vect(4,6)
res0: Vect[Int,9)

scala> Vect(1,3) add Vect(1)
<console>:26: error: type mismatch;
 found   : Vect[Int,shapeless.Succ[shapeless._0]]
 required: Vect[Int,shapeless.Succ[shapeless.Succ[shapeless.Succ[shapeless._0]]]]
       Vect(1,3) add Vect(1)

从本质上讲,所有归结为使用Sized和Nat进行类型约束.

(编辑:李大同)

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

    推荐文章
      热点阅读