scala的類型參數是否/能否具有C++ template一樣的表現能力,是否有其他語言具有類似能力?
C++的模板功能強大,能提供自定義類型和內置類型一致的語法和性能,但用C++寫程序太費神,各種類庫又喜歡造各種基礎設施的輪子,包括各種容器。同時C++的類編譯完後會在內存中散布得到處都是,在二進位層面上已經完全失去了類的結構,又沒有統一的ABI標準,所以動態的類型信息很少。
而另一方面,Java運行在虛擬機中,各種內省信息都保留完整,而且內存保護得很好,不會象C++那樣因為內存管理不善出問題。 scala是一個可能的選項,其抽象能力很強,但根據我這幾天的研究發現,scala的泛型遠不如C++靈活,限制很多,比如寫一個泛型的abs c++可以寫 template T abs(T x) {return x&>=0?x:-x;}按照同樣的思路寫scala就會出錯: def abs[T](x:T)=if x&>=0 then x else -x 即使加上類型約束:Numeric也不行,Numeric[T]是無法和其他基本數值類型一起運算的,硬要寫也行,但效率會因為box unbox下降很多。而且更令人髮指的是Numeric有fromInt卻沒有fromDouble 綜上泛型程序依然很難寫 要實現象scala那樣的抽象能力,同時又對泛型有很好的支持 有沒有更好的解決方案呢?
想要在 Scala 里自如地使用泛型數學運算, 就要先造一堆必要的抽象代數結構. 下面我們來考察一下題主這個 abs 函數. 這個函數要求它的泛型參數 T 具備:注意: 本答案的例子僅用作展示 Scala 中 typeclass 的用法. 事實上, 例子中 abs 函數並不是一個合適的對 "絕對值" 的抽象. 一種在數學上更為合理的抽象方法應該是: 定義一個 trait 叫 MetricSpace, 然後叫實現者提供 distance 方法.
- 逆元
- 偏序關係
為了以後方便, 乾脆我們直接定義一個 T 的加群和這個群中元素間的偏序關係好了. 下面開始定義加群和偏序, 即定義兩個 trait:
trait AdditiveGroup[T] {
def zero: T
def neg(x: T): T
def add(x: T, y: T)
}
trait Ordering[T] {
def ?(x: T, y: T): Boolean
def ?(x: T, y: T) = ?(y, x)
}
然後, 你的 abs 函數就可以這麼寫
def abs[T](v: T)(implicit g: AdditiveGroup[T], o: Ordering[T]) = {
if (o.?(v, g.zero)) v else g.inv(v)
}
implicit class Sweetener[T](val x: T) extends AnyVal {
def +(that: T)(implicit g: AdditiveGroup[T]) = g.add(x, that)
def unary_-(implicit g: AdditiveGroup[T]) = g.neg(x)
def ?(that: T)(implicit o: Ordering[T]) = o.?(x, that)
}
def abs[T](v: T)(implicit g: AdditiveGroup[T], o: Ordering[T]) = {
if (v ? g.zero) v else -v
}
看, 函數體和題主給的長得幾乎一樣了. 當然題主要是沒有代碼易讀性方面的強迫症, 這一步不是必要的. 我把 + 也塞到了 T 中, 是為了以後可以避免寫 g.add(x, y), 直接寫 x + y.
還沒完, 這個 abs 函數現在還不能工作, 因為我們沒有提供默認的加群和偏序關係. 提供的方法是實現之前定義的兩個 trait. 作為例子, 這裡為 Int 和 Double 提供默認實現:object Implicits {
implicit object IntAdditiveGroup extends AdditiveGroup[Int] {
override def zero: Int = 0
override def neg(x: Int): Int = -x
override def add(x: Int, y: Int): Int = x + y
}
implicit object IntOrdering extends Ordering[Int] {
override def ?(x: Int, y: Int): Boolean = x &<= y
}
implicit object DoubleAdditiveGroup extends AdditiveGroup[Double] {
override def zero: Double = 0.0
override def neg(x: Double): Double = -x
override def add(x: Double, y: Double): Double = x + y
}
implicit object DoubleOrdering extends Ordering[Double] {
override def ?(x: Double, y: Double): Boolean = x &<= y
}
implicit class Sweetener[T](val x: T) extends AnyVal {
def +(that: T)(implicit g: AdditiveGroup[T]) = g.add(x, that)
def ?(that: T)(implicit o: Ordering[T]) = o.?(x, that)
}
}
Scala 要求 implicit 的東西不能寫在 package 這一層. 所以我把這些 implicit 實現都包進了名為 Implicits 的一個 object. 注意我把 Sweetener 也粘進去了, 方便用的時候一行 import 全部導入.
到此, 題主所求的功能就實現好了. 我們來寫一個 main 函數試試:
import Implicits._
object Test {
def main(args: Array[String]) {
val intTest = abs(-4) // 得到 4
val doubleTest = abs(-4.5) // 得到 4.5
}
}
實現 Scala 下泛型的數學編程應該使用 typeclass。效率問題交由宏 (macro) 解決。實現這一功能的庫有 Spire (non/spire · GitHub)。它完全實現了你的需求,可以做到基本沒有效率損失。
import spire.algebra._
def abs[X](x: X)(implicit ev: Signed[X]): X = ev.abs(x)
如果覺得 Scala 的 typeclass 比較繁瑣,可以考慮 Haskell。
Typed Racket 吧
看樓上Typeclass已經被寫爛了哇!非Typeclass版本來一發,Magnet大法好啊
def abs(magnet: MyMagnet): magnet.Result = magnet()
sealed trait MyMagnet {
type Result
def apply(): Result
}
object MyMagnet {
implicit def fromInt(i: Int) = new MyMagnet {
type Result = Int
def apply(): Result = if (i &>= 0) i else -i
}
implicit def fromLong(i: Long) = new MyMagnet {
type Result = Long
def apply(): Result = if (i &>= 0) i else -i
}
implicit def fromFloat(i: Float) = new MyMagnet {
type Result = Float
def apply(): Result = if (i &>= 0) i else -i
}
implicit def fromDouble(i: Double) = new MyMagnet {
type Result = Double
def apply(): Result = if (i &>= 0) i else -i
}
implicit def fromBigInt(i: BigInt) = new MyMagnet {
type Result = BigInt
def apply(): Result = if (i &>= 0) i else -i
}
implicit def fromSomeInt(s: Option[Int]) = new MyMagnet {
type Result = Option[Int]
def apply(): Result = s match {
case Some(i) =&> Some(if (i &>= 0) i else -i)
case _ =&> None
}
}
implicit def fromListInt(l: List[Int]) = new MyMagnet {
type Result = List[Int]
def apply(): Result = l.map(i =&> if(i &>= 0) i else -i)
}
//...
}
println(abs(-1))
println(abs(-1L))
println(abs(-1.0))
println(abs(-1.0D))
println(abs(BigInt(-10000000)))
println(abs(Some(-100)))
println(abs(List(-1, 2, -3)))
很顯然你需要Rust
樓上幾位說的type class在標準庫中就有,直接用吧
scala&> import scala.math.Numeric
import scala.math.Numeric
scala&> import scala.math.Ordering.Implicits._
import scala.math.Ordering.Implicits._
scala&> import scala.math.Numeric.Implicits._
import scala.math.Numeric.Implicits._
scala&> def abs[T: Numeric](x:T) = if (x &>= implicitly[Numeric[T]].zero) x else -x
abs: [T](x: T)(implicit evidence$1: scala.math.Numeric[T])T
你說的不就是rustAbstraction without overhead: traits in Rust
Type Class可以解決這個泛型問題,不過寫起來比較麻煩,比如,
trait NumericLike[T] {
def abs(x: T): T}object NumericLike {
implicit object NumericLikeDouble extends NumericLike[Double] { def abs(x: Double) = if (x &>= 0) x else -x}
implicit object NumericLikeInt extends NumericLike[Int] {
def abs(x: Int) = if (x &>= 0) x else -x }}def abs[T](x: T)(implicit e: NumericLike[T]): T = e.abs(x)def abs[T](x: T)(implicit e: Numeric[T]): T = e.abs(x)推薦閱讀:
TAG:編程語言 | Scala | Java虛擬機JVM | C | 泛型Generic |