2010-06-21 14 views
9

Boolean'ın yanı sıra çeşitli sayısal türlerin değerlerini depolamak için C sınıfını uygulamak istiyorum. Ayrıca, bu sınıfın örneklerinde, gerekirse, Int --> Double ve Boolean -> Int, yani Boolean + Boolean, Int + Boolean, Boolean + Int, Int + Double, Double + Double vb. Mümkün olduğunda (Int veya Double) yazın.Sayısal türler arasında aritmetik sağlamak için örtülü dönüştürme nasıl ayarlanır?

Şimdiye kadar bu geldi:

Bu, bazı durumlarda çalışır
abstract class SemiGroup[A] { def add(x:A, y:A):A } 

class C[A] (val n:A) (implicit val s:SemiGroup[A]) { 
    def +[T <% A](that:C[T]) = s.add(this.n, that.n) 
} 

object Test extends Application { 
    implicit object IntSemiGroup extends SemiGroup[Int] { 
    def add(x: Int, y: Int):Int = x + y 
    } 

    implicit object DoubleSemiGroup extends SemiGroup[Double] { 
    def add(x: Double, y: Double):Double = x + y 
    } 

    implicit object BooleanSemiGroup extends SemiGroup[Boolean] { 
    def add(x: Boolean, y: Boolean):Boolean = true; 
    } 

    implicit def bool2int(b:Boolean):Int = if(b) 1 else 0 

    val n = new C[Int](10) 
    val d = new C[Double](10.5) 
    val b = new C[Boolean](true) 

    println(d + n) // [1] 
    println(n + n) // [2] 
    println(n + b) // [3] 
    // println(n + d) [4] XXX - no implicit conversion of Double to Int exists 
    // println(b + n) [5] XXX - no implicit conversion of Int to Boolean exists 
} 

(1, 2, 3) but does not için (4, 5). Bunun nedeni, tipin aşağıdan yukarıya doğru, ancak başka şekilde örtük olarak genişletilmemesidir.

def +[T, A <% T](that:C[T]):T = that.s.add(this.n, that.n) 

ama bu derleyici için this.n dönüştüremiyor öncelikle bu iki nedenden dolayı derleme değil: Bir bakıma,

def +[T <% A](that:C[T]) = s.add(this.n, that.n) 

nasılsa benzer görünümde olacaktır ortağı yöntemi olması gerekir yöntemde T yazın (A <% T numaralı bağı belirtmemize rağmen) ve ikinci olarak, this.n'u dönüştürdüyse bile, tür silme işleminden sonra iki + yöntemi belirsiz olur.

Üzgünüz, bu çok uzun. Herhangi bir yardım çok takdir edilecektir! Aksi halde, tüm operasyonlar arasındaki tüm işlemleri açık bir şekilde yazmam gerekiyor. Ekstra tipler eklemek zorunda olsaydım tüylü olurdum (Complex menüde ...).

Belki de tüm bunları bir arada gerçekleştirmek için başka bir yolu var mı? Bakıyorum basit bir şey varmış gibi hissediyorum.

Şimdiden teşekkürler!

cevap

6

Tamam o zaman, Daniel!

Boolean'ı yok saymak için çözümü kısıtladım ve yalnızca Numeric örneğine sahip zayıf En Düşük Üst Sınıra sahip AnyVals ile çalışıyorum. Bu kısıtlamalar keyfi olabilir, bunları kaldırabilir ve türleri arasındaki kendi zayıf uyumluluk ilişkinizi kodlayabilirsiniz - a2b ve a2c'un uygulanması bazı dönüşümler gerçekleştirebilir.

Onun ilginç miras (tip örtülü parametreleri geçirmeden (=> Baz Türetilmiş) ya da zayıf Konformansını taklit nasıl örtük parametreler dikkate almak. Onlar, gerçekten güçlü olan tipi inferencer size yardımcı oluyor özellikle.

Birincisi, İlgilendiğimiz A ve B türlerinin tüm çiftlerinin Zayıf En Yüksek Üst Sınırını temsil edecek bir tip sınıfına ihtiyacımız var.

sealed trait WeakConformance[A <: AnyVal, B <: AnyVal, C] { 
    implicit def aToC(a: A): C 

    implicit def bToC(b: B): C 
} 

object WeakConformance { 
    implicit def SameSame[T <: AnyVal]: WeakConformance[T, T, T] = new WeakConformance[T, T, T] { 
    implicit def aToC(a: T): T = a 

    implicit def bToC(b: T): T = b 
    } 

    implicit def IntDouble: WeakConformance[Int, Double, Double] = new WeakConformance[Int, Double, Double] { 
    implicit def aToC(a: Int) = a 

    implicit def bToC(b: Double) = b 
    } 

    implicit def DoubleInt: WeakConformance[Double, Int, Double] = new WeakConformance[Double, Int, Double] { 
    implicit def aToC(a: Double) = a 

    implicit def bToC(b: Int) = b 
    } 

    // More instances go here! 


    def unify[A <: AnyVal, B <: AnyVal, C](a: A, b: B)(implicit ev: WeakConformance[A, B, C]): (C, C) = { 
    import ev._ 
    (a: C, b: C) 
    } 
} 

örtülü değişken ev olarak temin etmek üzere kapalı değerlerin durumuna göre tip inferencer tarafından anlaşılabilen unify döner C tip yöntem.

Bunu, aşağıdaki gibi sarmalayıcı sınıf C'ye takabiliriz, ayrıca Numeric[WeakLub] gerektirir, böylece değerleri ekleyebiliriz.

case class C[A <: AnyVal](val value:A) { 
    import WeakConformance.unify 
    def +[B <: AnyVal, WeakLub <: AnyVal](that:C[B])(implicit wc: WeakConformance[A, B, WeakLub], num: Numeric[WeakLub]): C[WeakLub] = { 
    val w = unify(value, that.value) match { case (x, y) => num.plus(x, y)}; 
    new C[WeakLub](w) 
    } 
} 

Ve nihayet, hep birlikte koyarak:

object Test extends Application { 
    val n = new C[Int](10) 
    val d = new C[Double](10.5) 

    // The type ascriptions aren't necessary, they are just here to 
    // prove the static type is the Weak LUB of the two sides. 
    println(d + n: C[Double]) // C(20.5) 
    println(n + n: C[Int]) // C(20) 
    println(n + d: C[Double]) // C(20.5) 
} 

Test 
+0

Ahha! Bunun nasıl çalıştığını görüyorum - teşekkürler! 'Boolean' eklenmesi kolay oldu, ve 'Sayısal' LUB 'Karmaşık' için değiştirmek için çok zor olmayacak. Merak ediyorum - bu çözümü elinize geçirmiş görünüyorsunuz, bu bağlamda hangi bağlamda karşılaştınız? Ayrıca, bu çözümün performansını test etmeye çalıştım ve milyonlarca C'yi toplamaya çalıştım. [Int] 'ler, milyonlarca Int'ten daha beş kat daha yavaş görünüyor ... Başlamak için nasıl bir düşünce var? Bunu optimize et? – ostolop

+0

Bunu IRC'de @extempore ile yapılan bir tartışma sırasında oynadım, bu benim için belirli bir problemi çözmüyordu. Dolaylı olarak 5x overhead çok kötü gelmiyor. 'Unify' yöntemini kullanmak yerine doğrudan' wc.a2b' ve 'wc.a2c' diyebilirsiniz. Şu anda 'NumeriC# plus' girişleri ve çıktıları kutlanıyor, umarım Scala'nın gelecekteki bir sürümü de bu problemi çözmek için bir yol bulacaktır. – retronym

+0

@retronym Aslında ... Bu tartışmayı başlattım. :-) –

3

Bunu yapmak için bir way var, ancak bu çözümü yazdığından beri bunu açıklamak için retronym'a bırakacağım. :-)

İlgili konular