2010-06-21 4 views
9

부울뿐만 아니라 다양한 숫자 형식의 값을 저장하는 클래스 C을 구현하고 싶습니다. 또한,이 클래스의 인스턴스를 필요에 따라 Int --> DoubleBoolean -> Int으로 변환 할 수 있습니다. 즉, Boolean + Boolean, Int + Boolean, Boolean + Int, Int + Double, Double + Double 등을 추가 할 수 있고 가능한 한 가장 작은 값을 반환 할 수 있습니다. 가능할 때마다 (Int 또는 Double)를 입력하십시오.숫자 형식 간의 산술을 허용하는 암시 적 변환을 설정하는 방법?

지금까지 나는이 함께했다 :

이 경우 작동
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)하지만하지 않습니다 (4, 5). 그 이유는 형식이 낮은 것부터 높은 것까지 묵시적으로 넓어 지지만 다른 방법은 아닙니다.

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

하지만 컴파일러가 this.n 변환 할 수 없습니다 먼저 것으로, 두 가지 이유가 컴파일되지 않습니다 : 방법,

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

어떻게 든 같이 보일 것이다 파트너 방법을 가질 필요가 방법에 두 번째로 this.n을 변환 할 수 있다고해도 형식 지우기 후에 두 개의 + 메서드가 모호 해집니다.

죄송합니다. 너무 오래되었습니다. 어떤 도움을 많이 주시면 감사하겠습니다! 그렇지 않으면 명시 적으로 모든 유형 간의 모든 작업을 작성해야합니다. 그리고 여분의 유형을 추가해야한다면 털이 될 것입니다 (Complex은 메뉴 옆에 있습니다 ...).

누구나이 모든 것을 모두 달성 할 수있는 다른 방법이 있습니까? 간과 할 수없는 무언가가있는 것 같은 느낌.

미리 감사드립니다.

답변

6

좋아요, Daniel!

나는 부울을 무시하도록 솔루션을 제한했으며, ​​Numeric의 인스턴스가있는 약한 최소 상단을 가진 AnyVals에서만 작동합니다. 이러한 제한은 임의적입니다. 유형을 제거하고 유형간에 자신의 약한 적합성 관계를 인코딩 할 수 있습니다. a2ba2c의 구현은 일부 변환을 수행 할 수 있습니다.

그 흥미는 상속 (유형의 암시 적 매개 변수를 전달 (=> 자료 파생) 또는 약한 적합성을 시뮬레이션 할 수있는 방법을 암시 적 매개 변수를 고려해야합니다. 그들은 정말 강력한 타입 inferencer가 당신을 도와줍니다 특히.

먼저, 우리가 관심을 갖고있는 모든 유형 쌍인 AB의 Weak Least Upper Bound를 나타 내기 위해 유형 클래스가 필요합니다.

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) 
    } 
} 

내재적 인자 ev로 제공 암시 값의 이용 가능성에 기초하여 상기 입력 inferencer 의해 파악되는 unifyC 복귀를 입력하는 방법.

래퍼 클래스 C에 다음과 같이 플러그 할 수 있으며 값을 추가 할 수 있도록 Numeric[WeakLub]이 필요합니다.

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) 
    } 
} 

그리고 마지막으로, 모두 함께 넣어 :

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을! 나는 이것이 어떻게 작동하는지 보았다 - 고마워! 'Boolean'을 추가하는 것이 쉽고, Numer'의 LUB은'Complex'를 위해 너무 쉽게 변경되지 않습니다. 궁금한 점이 있습니다.이 솔루션을 슬리브에 넣은 것 같은데, 어떤 상황에서이 문제를 겪었습니까? 또한이 솔루션의 성능을 테스트하고 백만개의'C [Int] '가 백만 분의'Int's보다 약 5 배 느린 것으로 보입니다. 시작하는 방법에 대한 생각 이것을 최적화할까요? – ostolop

+0

IRC에서 @extempore와 토론하는 동안이 문제를 해결했지만 문제가 해결되지 않았습니다. 간접비를 고려할 때 5 배의 오버 헤드가 나쁘지는 않습니다. 'unify' 메소드를 사용하는 대신'wc.a2b'와'wc.a2c'를 직접 호출 할 수 있습니다. 현재 'NumeriC# plus'의 입력과 출력은 박스로되어 있습니다. 앞으로의 스칼라 버전에서는 그 문제를 @specialize 할 수있는 방법을 찾을 수있을 것입니다. – retronym

+0

@retronym 사실 ... 나는 그 토론을 시작했다. :-) –

3

여기에는 way이 있지만이 솔루션을 작성한 이후로 설명하려면 retronym으로 남겨 두겠습니다. :-)

관련 문제