首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >在编译时添加两个相同大小的列表

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

Stack Overflow用户
提问于 2016-01-20 09:08:51
回答 2查看 456关注 0票数 5

在Idris中,我可以通过将两个大小相同的向量相加:

代码语言:javascript
运行
复制
module MatrixMath

import Data.Vect

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

在REPL上编译后:

代码语言:javascript
运行
复制
*MatrixMath> :l MatrixMath.idr 
Type checking ./MatrixMath.idr

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

代码语言:javascript
运行
复制
*MatrixMath> addHelper [1,2,3] [4,5,6]
[5, 7, 9] : Vect 3 Integer

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

代码语言:javascript
运行
复制
*MatrixMath> addHelper [1,2,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写这个呢?

EN

回答 2

Stack Overflow用户

发布于 2016-01-20 10:42:14

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

对实现的第一次尝试可能如下所示

代码语言:javascript
运行
复制
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))

以及它的用法:

代码语言:javascript
运行
复制
scala> add(Vect(3)(1, 2, 3), Vect(3)(4, 5, 6))
res0: shapeless.Sized[Vector[Int],shapeless.Succ[shapeless.Succ[shapeless.Succ[shapeless._0]]]] = Vector(5, 7, 9)

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

虽然这看起来是可行的,但它有一个严重的缺点-你必须确保提供的长度和参数的数量匹配,否则你会得到一个运行时错误。

代码语言:javascript
运行
复制
scala> Vect(1)(1, 2, 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提供的那么好,但它是可用的):

代码语言:javascript
运行
复制
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], N]((left, right).zipped.map(A.plus))

// ...

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

scala> add(Sized(1, 2, 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, 2, 3))(Sized(1))

但我们可以走得更远。Shapeless还提供了元组和Sized之间的转换,所以我们可以这样写:

代码语言:javascript
运行
复制
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, 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))

这是可行的,大小是从提供的元组中推断出来的:

代码语言:javascript
运行
复制
scala> add(Vect(1, 2, 3), Vect(4, 5, 6))
res0: shapeless.Sized[Vector[Int],shapeless.Succ[shapeless.Succ[shapeless.Succ[shapeless._0]]]] = Vector(5, 7, 9)

scala> add(Vect(1, 2, 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, 2, 3))(Vect(4, 5, 6, 7))

不幸的是,这个例子中的语法只适用于一个叫做参数自适应的特性,在这个特性中,scalac会自动将多个参数从Vect转换成我们需要的元组。由于这个“特性”也可能导致非常糟糕的bug,我发现自己几乎总是用-Yno-adapted-args禁用它。使用这个标志,我们必须自己显式地编写元组:

代码语言:javascript
运行
复制
scala> Vect(1, 2, 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, 2, 3
 after adaptation: Vect((1, 2, 3): (Int, Int, Int))
       Vect(1, 2, 3)
           ^
<console>:26: error: too many arguments for method Vect: (xs: (Int, Int, Int))(implicit toSized: shapeless.ops.tuple.ToSized[(Int, Int, Int),Vector])toSized.Out
       Vect(1, 2, 3)
           ^

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

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

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

代码语言:javascript
运行
复制
scala> Vect((1, 2, 3, 4, 5, 6, 7, 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, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23))

那么,我们能得到稍微好一点的语法吗?事实证明,我们可以。Shapeless可以为我们做包装,使用HList:

代码语言:javascript
运行
复制
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 <: 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))

而且它是有效的:

代码语言:javascript
运行
复制
scala> add(Vect(1, 2, 3))(Vect(4, 5, 6))
res0: shapeless.Sized[Vector[Int],shapeless.Succ[shapeless.Succ[shapeless.Succ[shapeless._0]]]] = Vector(5, 7, 9)

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

scala> Vect(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23)
res2: shapeless.Sized[scala.collection.immutable.Vector[Int],shapeless.Succ[shapeless.Succ[... long type elided... ]]] = Vector(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23)

您可以更进一步,将Sized包装在您自己的类中,例如

代码语言:javascript
运行
复制
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]): 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, 2, 3) add Vect(4, 5, 6)
res0: Vect[Int,shapeless.Succ[shapeless.Succ[shapeless.Succ[shapeless._0]]]] = Vector(5, 7, 9)

scala> Vect(1, 2, 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, 2, 3) add Vect(1)

从本质上讲,所有这些都归结为使用SizedNat作为类型约束。

票数 7
EN

Stack Overflow用户

发布于 2016-01-20 10:10:10

Shapeless你能在这方面帮你吗:

代码语言:javascript
运行
复制
import shapeless._
import syntax.sized._

def row(cols : Seq[String]) = cols.mkString("\"", "\", \"", "\"")

def csv[N <: Nat](hdrs : Sized[Seq[String], N], rows : List[Sized[Seq[String], N]]) =
    row(hdrs) :: rows.map(row(_))

val hdrs = Sized("Title", "Author") // Sized[IndexedSeq[String], _2]
val rows = List(                  // List[Sized[IndexedSeq[String], _2]]
  Sized("Types and Programming Languages", "Benjamin Pierce"),
  Sized("The Implementation of Functional Programming Languages", "Simon Peyton-Jones")
)

 // hdrs and rows statically known to have the same number of columns
val formatted = csv(hdrs, rows)

注意csv方法是如何通过N <: Nat约束Sized的,与您在示例中通过Num n约束的方式相同。

我从Shapeless examples复制了这段代码,如果它不是这样编译的,我很可能遗漏了一些东西。

票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/34889808

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档