scala – 在编译时添加两个相同大小的列表
参见英文答案 >
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而不是其他构造函数. 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进行类型约束. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |