gpt4 book ai didi

c++ - scala 是否提供类似 C++ 模板的东西?

转载 作者:可可西里 更新时间:2023-11-01 18:02:59 27 4
gpt4 key购买 nike

我来自 C++ 并试图围绕 Scala 的类型系统。

考虑以下 C++ 模板类:

template<class T>
class Point2
{
Point2( T x, T y ) :
x(x),
y(y)
{}

T x;
T y;
Point2<T> operator+( Point<T> const& other ) const
{
return Point<T>(x+other.x, y+other.y);
}
T sumComponents() const { return x+y; }
}

Point2<Double> p0(12.3, 45.6)
Point2<Double> p1(12.3, 45.6)
Point2<Double> p = p1+p2
Double d = p1.sumComponents()

我发现我想写这样的东西:
case class Point2[ T ] (x:T, y:T) {
def +() Point2[T]: = x+y
def sumComponents() T: = x+y
}

或者,(因为编译有问题),
trait Addable[T] {   // Require T supports the + operatory
def +( that:T ):T
}
case class Point2[ T<:Addable[T] ] (x:T, y:T) {
def +() Point2[T]: = x+y
def sumComponents() T: = x+y
}

这同样有问题,因为我不能要求 Double 来扩展 Addable。

通常,我发现 Scala 的类型系统可以处理一组我不太了解的约束。

在 Scala 中实现上述内容的惯用方式是什么?

什么是 C++ 模板程序员理解 Scala 中泛型限制的正确方法? (为什么我不能在 Scala 中执行此操作?例如,是因为泛型是在实例化之前编译的吗?)

最佳答案

What's the idiomatic way of implementing the above in scala?



通过指定 T 的适当要求,或使用类型类来提供所需的行为。稍后我会回到这个话题。

And what's the right way for C++ template programmers to understand the limits of generics in scala? (why can't I do this in scala? e.g. Is it because generics are compiled before being instaniated?)



C++ 模板在使用站点“在”编译,并且为模板的每个参数组合生成不同的代码。因此,如果您将上面的类与 int 一起使用和 double ,你会得到两个不同的 Point2类编译。

基本上,C++ 模板是宏,尽管远不及 #define 那样愚蠢。宏。事实上,C++ 模板是图灵完备的。也许将来有可能完成一些等效的事情,即将推出的宏功能计划用于 Scala 2.11 及更高版本,但现在让我们忽略它。

类型参数(Java 泛型的 Scala 等价物)不会改变代码的编译方式。参数化类在编译时生成字节码,而不是在使用时生成字节码。因此,当我们实例化 Point2 时与 Double ,生成字节码来不及了。

这意味着参数化类生成的代码必须与类可以实例化的所有类型兼容。

这就是问题的根源:在 T 上调用的任何方法必须知道存在于 T当时 Point2被编译。因此, T必须定义为具有定义此类方法的特征或类的上限,如您在示例中所示。

当然,正如您正确指出的那样,这并不总是可能的,这就是类型类的用武之地。类型类是定义了一组行为的一组类型。在 Scala 中实现的类型类被定义为其实例定义其他类的行为的类。

在您提供的示例中,您将使用 Numeric类型类,或 Fractional如果您还需要分数除法,请键入 class。类型类使用的一个简单示例是:
scala> import scala.math.Numeric
import scala.math.Numeric

scala> def sum[T](x: T, y: T)(implicit num: Numeric[T]): T = num.plus(x, y)
sum: [T](x: T, y: T)(implicit num: scala.math.Numeric[T])T

或者,使用称为“上下文边界”的特殊符号,
scala> def sum[T : Numeric](x: T, y: T): T = implicitly[Numeric[T]].plus(x, y)
sum: [T](x: T, y: T)(implicit evidence$1: scala.math.Numeric[T])T

书写方式 T : Numeric可以读作 T这样就有一个 Numeric[T] 的隐式实例可用的。代码 implicitly[X]返回类型 X 的隐式值如果可以找到(或在编译时失败)。

现在,请注意在 x 上没有调用任何方法和 y -- 相反,我们在 num 上调用方法其类(class)是 Numeric[T] .类(class) Numeric[T]有一个方法 plus知道如何添加两个 T s。

因为我们需要的是类型类实例,所以可以很容易地添加新类型来满足类型类。人们可以很容易地声明一个 Numeric Point2 的类型类(假设它的所有方法都可以实现):
class Point2Numeric[T](implicit num: Numeric[T]) extends Numeric[Point2[T]] {
def plus(x: Point2[T], y: Point2[T]): Point2[T] = x + y
// etc
}
implicit def ToPoint2Numeric[T : Numeric] = new Point2Numeric[T]

有了它,那么对于任何 T其中有一个 Numeric[T] ,也会有 Numeric[Point2[T]] .

在普通类型继承(类型上限)之后,类型类是 Scala 中最常见的类型约束形式。还有其他更复杂的形式,对于它们是类型类还是不同的东西——例如磁铁模式,有一些讨论。看 shapeless举个例子,说明人们可以采取这些措施的程度。

另一种曾经很常见但现在被更谨慎使用的类型约束是 View 边界。我不会详细介绍(实际上,搜索上下文边界和 View 边界以从我自己那里找到有关它的详细答案),但它们可用于使类型类在使用时更具可读性。例如:
scala> import scala.math.Numeric.Implicits._
import scala.math.Numeric.Implicits._

scala> def sum[T : Numeric](x: T, y: T): T = x + y
sum: [T](x: T, y: T)(implicit evidence$1: scala.math.Numeric[T])T

导入的定义包含隐式转换,可以使用类型 T 的值。其中有一个 Numeric[T]就好像他们自己有类似 + 的方法一样或 - .

最后要注意的是,重要的是要意识到这会经历许多间接级别,因此可能不太适合高性能代码。

关于c++ - scala 是否提供类似 C++ 模板的东西?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18347167/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com