2008-09-15 3 views
25

में जेनिक्स का उपयोग करके एक गणित पुस्तकालय बनाना क्या गणित पुस्तकालय बनाने के लिए जेनेरिक का उपयोग करने का कोई व्यवहार्य तरीका है जो डेटा स्टोर करने के लिए चुने गए मूल प्रकार पर निर्भर नहीं है?सी #

दूसरे शब्दों में, मान लीजिए कि मैं एक फ्रैक्शन वर्ग लिखना चाहता हूं। अंश को दो इंट्स या दो युगल या व्हाट्नॉट द्वारा दर्शाया जा सकता है। महत्वपूर्ण बात यह है कि बुनियादी चार अंकगणितीय परिचालन अच्छी तरह परिभाषित हैं। तो, मैं Fraction<int> frac = new Fraction<int>(1,2) और/या Fraction<double> frac = new Fraction<double>(0.1, 1.0) लिखने में सक्षम होना चाहता हूं।

दुर्भाग्य से चार बुनियादी परिचालनों (+, -, *, /) का प्रतिनिधित्व करने वाला कोई इंटरफ़ेस नहीं है। क्या किसी को इसे कार्यान्वित करने का एक व्यावहारिक, व्यवहार्य तरीका मिला है?

उत्तर

25

यहाँ बाहर ऑपरेटरों सार है कि अपेक्षाकृत दर्दरहित है के लिए एक रास्ता है।

abstract class MathProvider<T> 
    { 
     public abstract T Divide(T a, T b); 
     public abstract T Multiply(T a, T b); 
     public abstract T Add(T a, T b); 
     public abstract T Negate(T a); 
     public virtual T Subtract(T a, T b) 
     { 
      return Add(a, Negate(b)); 
     } 
    } 

    class DoubleMathProvider : MathProvider<double> 
    { 
     public override double Divide(double a, double b) 
     { 
      return a/b; 
     } 

     public override double Multiply(double a, double b) 
     { 
      return a * b; 
     } 

     public override double Add(double a, double b) 
     { 
      return a + b; 
     } 

     public override double Negate(double a) 
     { 
      return -a; 
     } 
    } 

    class IntMathProvider : MathProvider<int> 
    { 
     public override int Divide(int a, int b) 
     { 
      return a/b; 
     } 

     public override int Multiply(int a, int b) 
     { 
      return a * b; 
     } 

     public override int Add(int a, int b) 
     { 
      return a + b; 
     } 

     public override int Negate(int a) 
     { 
      return -a; 
     } 
    } 

    class Fraction<T> 
    { 
     static MathProvider<T> _math; 
     // Notice this is a type constructor. It gets run the first time a 
     // variable of a specific type is declared for use. 
     // Having _math static reduces overhead. 
     static Fraction() 
     { 
      // This part of the code might be cleaner by once 
      // using reflection and finding all the implementors of 
      // MathProvider and assigning the instance by the one that 
      // matches T. 
      if (typeof(T) == typeof(double)) 
       _math = new DoubleMathProvider() as MathProvider<T>; 
      else if (typeof(T) == typeof(int)) 
       _math = new IntMathProvider() as MathProvider<T>; 
      // ... assign other options here. 

      if (_math == null) 
       throw new InvalidOperationException(
        "Type " + typeof(T).ToString() + " is not supported by Fraction."); 
     } 

     // Immutable impementations are better. 
     public T Numerator { get; private set; } 
     public T Denominator { get; private set; } 

     public Fraction(T numerator, T denominator) 
     { 
      // We would want this to be reduced to simpilest terms. 
      // For that we would need GCD, abs, and remainder operations 
      // defined for each math provider. 
      Numerator = numerator; 
      Denominator = denominator; 
     } 

     public static Fraction<T> operator +(Fraction<T> a, Fraction<T> b) 
     { 
      return new Fraction<T>(
       _math.Add(
        _math.Multiply(a.Numerator, b.Denominator), 
        _math.Multiply(b.Numerator, a.Denominator)), 
       _math.Multiply(a.Denominator, b.Denominator)); 
     } 

     public static Fraction<T> operator -(Fraction<T> a, Fraction<T> b) 
     { 
      return new Fraction<T>(
       _math.Subtract(
        _math.Multiply(a.Numerator, b.Denominator), 
        _math.Multiply(b.Numerator, a.Denominator)), 
       _math.Multiply(a.Denominator, b.Denominator)); 
     } 

     public static Fraction<T> operator /(Fraction<T> a, Fraction<T> b) 
     { 
      return new Fraction<T>(
       _math.Multiply(a.Numerator, b.Denominator), 
       _math.Multiply(a.Denominator, b.Numerator)); 
     } 

     // ... other operators would follow. 
    } 

आप एक प्रकार है कि आप उपयोग को लागू करने में विफल रहते हैं, तो आप एक विफलता रनटाइम पर बजाय संकलन समय पर (जो बुरा है) मिल जाएगा। MathProvider<T> कार्यान्वयन की परिभाषा हमेशा एक जैसी (भी खराब) होगी। मैं सुझाव दूंगा कि आप इसे सी # में करने से बचें और एफ # या किसी अन्य भाषा का उपयोग इस अवशोषण के स्तर के लिए उपयुक्त है।

संपादित करें:Fraction<T> के लिए जोड़ने और घटाने की निश्चित परिभाषाएं। एक और दिलचस्प और सरल बात यह है कि एक मैथप्रोवाइडर लागू होता है जो एक सार वाक्यविन्यास पेड़ पर चलता है। यह विचार तत्काल भिन्नता जैसे चीजों को करने के लिए इंगित करता है: http://conal.net/papers/beautiful-differentiation/

+0

अच्छा साफ :) –

+1

सामान्य तरीके से मुझे लगता है कि MathProvider को एक इंटरफ़ेस में बनाया जाना चाहिए और सामान्य इंटरफ़ेस विधि में घटाना चाहिए या इसे एक्सटेंशन विधि के रूप में कार्यान्वित किया जा सकता है। दूसरी तरफ इसे ओवरराइड करने की अनुमति नहीं होगी। – dalle

+0

मुझे आपके समाधान के प्रदर्शन के बारे में आश्चर्य है ... यह सब कुछ केवल तभी काम करता है जब सब कुछ रेखांकित किया गया हो ... –

1

सबसे पहले, आपकी कक्षा को जेनेरिक पैरामीटर को प्राइमेटिव्स (सार्वजनिक वर्ग फ्रैक्शन जहां टी: स्ट्रक्चर, नया()) सीमित करना चाहिए।

दूसरा, आपको शायद implicit cast overloads बनाने की आवश्यकता होगी ताकि आप कंपाइलर रोने के बिना एक प्रकार से दूसरे में कास्टिंग को संभाल सकें।

तीसरा, आप चार मूल ऑपरेटरों को अधिभारित कर सकते हैं साथ ही विभिन्न प्रकार के अंशों को जोड़ते समय इंटरफ़ेस को अधिक लचीला बना सकते हैं।

आखिरकार, आपको यह समझना होगा कि आप अंकगणित ओवर और अंडरफ्लो को कैसे प्रबंधित कर रहे हैं। एक अच्छी लाइब्रेरी बेहद स्पष्ट होने जा रही है कि यह कैसे बहती है; अन्यथा आप विभिन्न अंश प्रकारों के संचालन के नतीजे पर भरोसा नहीं कर सकते हैं।

+1

समस्या यह है कि मैं इस तरह की रकम भी नहीं कर सकता क्योंकि structs में अतिरिक्त ऑपरेटर परिभाषित नहीं है। – Sklivvz

+0

http://msdn.microsoft.com/en-us/library/aa691324(VS.71)।एएसपीएक्स "उपयोगकर्ता परिभाषित कार्यान्वयन कक्षाओं और structs में ऑपरेटर घोषणाओं सहित – Will

6

मेरा मानना ​​है कि यह आपके सवाल का जवाब:

http://www.codeproject.com/KB/cs/genericnumerics.aspx

+1

उस समाधान, और अन्य उपलब्ध (जैसे एमिट का उपयोग करना) वास्तव में बिल्कुल साफ नहीं हैं, इसलिए यह नहीं है कि मैं क्या देख रहा था। लेकिन वैसे भी धन्यवाद :) – Sklivvz

2

यहां सामान्य सूक्ष्म समस्या है जो सामान्य प्रकारों के साथ आता है। मान लीजिए कि एल्गोरिदम में विभाजन शामिल है, समीकरणों की एक प्रणाली को हल करने के लिए गॉसियन उन्मूलन कहें। यदि आप पूर्णांक में गुजरते हैं, तो आपको एक गलत जवाब मिलेगा क्योंकि आप पूर्णांक विभाजन करेंगे। लेकिन यदि आप डबल तर्कों में गुजरते हैं जो पूर्णांक मान होते हैं, तो आपको सही उत्तर मिल जाएगा।

चोलस्की कारक के रूप में, वही चीज वर्ग की जड़ों के साथ होती है। एक पूर्णांक मैट्रिक्स को फैक्टरिंग गलत हो जाएगा, जबकि पूर्णांक मान होने वाले युगल के मैट्रिक्स को ठीक करना ठीक होगा।