2011-01-19 6 views
5

this question में चर्चा पर बिल्डिंग, कोई भी कोड, या कोड के लिंक प्रदान कर सकता है, जो NumericLiteralX मॉड्यूल (जैसे this one) का पूर्ण कार्यान्वयन दिखा रहा है? मैं विशेष रूप से FromInt32/64 के NumericLiteralX मॉड्यूल के लिए एक प्रभावी कार्यान्वयन में रूचि रखता हूं जो सामान्य संख्यात्मक संचालन को सुविधाजनक बनाता है।पूर्ण, कुशल न्यूमेरिक लिटरल मॉड्यूल कार्यान्वयन

module NumericLiteralG = 
    let inline FromZero() = LanguagePrimitives.GenericZero 
    let inline FromOne() = LanguagePrimitives.GenericOne 
    let inline FromInt32 (n:int) = 
     let one : ^a = FromOne() 
     let zero : ^a = FromZero() 
     let n_incr = if n > 0 then 1 else -1 
     let g_incr = if n > 0 then one else (zero - one) 
     let rec loop i g = 
      if i = n then g 
      else loop (i + n_incr) (g + g_incr) 
     loop 0 zero 

यह कैसे सुधार हुआ है और पूरा किया जा सकता: यह एक शायद अक्षम कार्यान्वयन ऊपर उल्लिखित सवाल से लिया है?

उत्तर

14

मैं बस FromInt32 को संबोधित करूंगा। एक आदर्श दुनिया में, हम यह बस के रूप में

let inline FromInt32 i = 
    ((^t or int) : (static member op_Explicit : int -> ^t) i) 

जो स्थिर बाधाओं का उपयोग सुनिश्चित करने के लिए है कि हम int से एक स्पष्ट रूपांतरण इनलाइन सकता है के रूप में परिभाषित कर सकते हैं। दुर्भाग्यवश, इसके साथ दो समस्याएं हैं। पहला यह है कि वाक्यविन्यास अमान्य है - आपके पास सदस्य की बाधा के "स्थैतिक-टाइपर्स" हिस्से में एक ठोस प्रकार (जैसे int) नहीं हो सकता है। हम इस के चारों ओर एक सहायक समारोह

let inline cvt i = ((^t or ^u) : (static member op_Explicit : ^u -> ^t) i) 
let inline FromInt32 (i:int) = cvt i 

को परिभाषित इन कार्यों inlined कर रहे हैं दोनों के बाद से काम कर सकते हैं, इस पहले प्रयास की तुलना में किसी भी कम कुशल नहीं है, यह सिर्फ wordier है।

यहाँ है जहाँ हम दूसरी समस्या में पड़: यह असली op_Explicit परिभाषाओं (या op_Implicit है, जो विशेष रूप से संकलक द्वारा इलाज किया जाता है ताकि यह op_Explicit द्वारा सम्मिलित किया जाता है) के लिए काम करेंगे। तो (10G : bigint) को रेखांकित किया जाएगा जैसे आपने System.Numerics.BigInt.op_Implicit 10 लिखा था, जो कि हम जितना सक्षम हो उतना कुशल है। हालांकि, एफ # भी कई आदिम प्रकार के लिए op_Explicit (int से float, byte, आदि के लिए रूपांतरण के लिए जैसे) simulates, और के बाद से FromInt32 की परिभाषा इन सदस्यों के अस्तित्व पर निर्भर करता है यह (है कि, (10G : float) और यहां तक ​​कि है कार्यावधि में असफल हो जायेगी (10G : int) संकलित होगा लेकिन निष्पादित होने पर अपवाद फेंक देगा)। आदर्श रूप से एफ # का भविष्य संस्करण इसे काम करने में सक्षम बनाता है, लेकिन एफ # 2.0 के रूप में, हमें एक कामकाज के साथ आने की आवश्यकता होगी।

यह अच्छा होगा यदि हम करने के लिए कैसे एफ # कोर पुस्तकालय इस तरह की समस्या है, जो विशेष आवरण गर्भित ऑपरेटरों के सभी की आवश्यकता होगी, लेकिन सब कुछ में परिणाम होगा संभालती एक समान दृष्टिकोण इस्तेमाल कर सकते हैं हो सकता है सही दक्षता के साथ inlined जा रहा है:

let inline FromInt32 (i : int) : ^t = 
    cvt i 
    when ^t : int = int i 
    when ^t : float = float i 
    when ^t : byte = byte i 
    ... 

हालांकि, एफ # संकलक एक "Static optimization conditionals are only for use within the F# library" संदेश के साथ इस को खारिज कर दिया (और गुप्त --compiling-fslib ध्वज के साथ संकलन केवल बातें बदतर बना देता है :))।

इसके बजाए, हमें रनटाइम पर समान कुछ प्राप्त करने के लिए संकेत के कुछ अतिरिक्त परतों का उपयोग करने की आवश्यकता है। सबसे पहले, हम एक सामान्य प्रकार का एक स्थिर सदस्य का उपयोग करके रूपांतरण कार्यों के लिए प्रकार की एक क्रम मानचित्रण पैदा हो जाएगी:

type IntConverterDynamicImplTable<'t>() = 
    static let result : int -> 't = 
    let ty = typeof< 't> //' 
    if ty.Equals(typeof<sbyte>)  then sbyte  |> box |> unbox 
    elif ty.Equals(typeof<int16>)  then int16  |> box |> unbox 
    elif ty.Equals(typeof<int32>)  then int  |> box |> unbox 
    elif ty.Equals(typeof<int64>)  then int64  |> box |> unbox 
    elif ty.Equals(typeof<nativeint>) then nativeint |> box |> unbox 
    elif ty.Equals(typeof<byte>)  then byte  |> box |> unbox 
    elif ty.Equals(typeof<uint16>)  then uint16  |> box |> unbox 
    elif ty.Equals(typeof<char>)  then char  |> box |> unbox 
    elif ty.Equals(typeof<uint32>)  then uint32  |> box |> unbox 
    elif ty.Equals(typeof<uint64>)  then uint64  |> box |> unbox 
    elif ty.Equals(typeof<unativeint>) then unativeint |> box |> unbox 
    elif ty.Equals(typeof<decimal>) then decimal |> box |> unbox 
    elif ty.Equals(typeof<float>)  then float  |> box |> unbox 
    elif ty.Equals(typeof<float32>) then float32 |> box |> unbox 
    else 
     let m = 
     try ty.GetMethod("op_Implicit", [| typeof<int> |]) 
     with _ -> ty.GetMethod("op_Explicit", [| typeof<int> |]) 
     let del = 
     System.Delegate.CreateDelegate(typeof<System.Func<int,'t>>, m) 
     :?> System.Func<int,'t> 
     del.Invoke |> box |> unbox 
    static member Result = result 

यह है कि हम क्या पिछले प्रयास में स्थिर अनुकूलन सशर्त, साथ प्राप्त करने के लिए कोशिश कर रहे थे के समान है , लेकिन यह संकलन समय पर मूल्यांकन किए जा रहे सब कुछ के बजाय रनटाइम को स्थगित कर दिया गया है।अब हम सिर्फ इस प्रकार का उपयोग करने के लिए कुछ मान निर्धारित करने की जरूरत है:

let inline constrain< ^t, ^u when (^t or ^u) : (static member op_Explicit : ^t -> ^u)>() =() 
let inline FromInt32 i : ^t = 
    constrain<int, ^t>() 
    IntConverterDynamicImplTable.Result i 

यहाँ, constrain समारोह बस सुनिश्चित करें कि FromInt32 केवल जहां पूर्णांक से एक स्पष्ट रूपांतरण वहाँ प्रकार के लिए लागू किया जा सकता है बनाने के लिए या एक प्रयोग किया जाता है (संकलक द्वारा अनुकरण)। FromInt32 के भीतर constrain() पर वास्तविक कॉल संकलन के दौरान अनुकूलित हो जाना चाहिए।

इस दृष्टिकोण के साथ

, (10G : bigint)IntConverterDynamicImplTable<bigint>.Result 10 की तरह कुछ करने के लिए संकलित होगा मिलता है, और IntConverterDynamicTable<bigint>.Result(System.Func<int,bigint>(bigint.op_Implicit)).Invoke के लिए एक मूल्य के बराबर होगा (लेकिन संचित, ताकि प्रतिनिधि केवल एक बार बनाई गई है)। इसी प्रकार, (10G : int64)IntConverterDynamicImplTable<int64>.Result 10 पर संकलित होगा, और IntConverterDynamicTable<int64>.Result में रूपांतरण फ़ंक्शन (int64 : int -> int64) के समतुल्य मूल्य होगा, इसलिए कुछ विधि कॉल के ओवरहेड हैं, लेकिन समग्र प्रदर्शन बहुत अच्छा होना चाहिए।

संपादित

हालांकि, अगर आप सिर्फ FromInt32 और FromInt64 समय देने के लिए की एक भोली कार्यान्वयन से अधिक कुशल कुछ के लिए देख रहे हैं हे (एन), यहाँ एक संस्करण है जो अभी भी अपेक्षाकृत सरल और केवल है लेता हे (लॉग एन) समय:

module SymmetricOps = 
    let inline (~-) (x:'a) : 'a = -x 
    let inline (+) (x:'a) (y:'a) : 'a = x + y 
    let inline (-) (x:'a) (y:'a) : 'a = x - y 
    let inline (*) (x:'a) (y:'a) : 'a = x * y 
    let inline (/) (x:'a) (y:'a) : 'a = x/y 
    let inline (%) (x:'a) (y:'a) : 'a = x % y 

module NumericLiteralG = 
    open SymmetricOps 
    let inline FromOne() = LanguagePrimitives.GenericOne 
    let inline FromZero() = LanguagePrimitives.GenericZero 
    let rec compute zero one two (/) (%) Two (+) (-) (*) pow2 rest n = 
    if n = zero then rest 
    else 
     let rest' = 
     let nmod2 = n % two 
     if nmod2 = zero then rest 
     elif nmod2 = one then rest + pow2 
     else rest - pow2 
     compute zero one two (/) (%) Two (+) (-) (*) (Two * pow2) rest' (n/two) 
    let inline FromInt32 i = compute 0 1 2 (/) (%) (FromOne() + FromOne()) (+) (-) (*) (FromOne()) (FromZero()) i 
    let inline FromInt64 i = compute 0L 1L 2L (/) (%) (FromOne() + FromOne()) (+) (-) (*) (FromOne()) (FromZero()) i 
+0

वाह। महान स्पष्टीकरण के लिए धन्यवाद। – Daniel

+0

इस पल के लिए अनुदान कि कोई भी प्राणघातक इसे लागू करने की संभावना नहीं है, क्या मनमाना सामान्य संख्या व्यक्त करने का एक आसान तरीका है? 'जेनेरिकझेरो' और 'जेनेरिकऑन' दिए गए हैं, लेकिन 'जेनेरिकएन' के बारे में क्या? जेनेरिक न्यूमेरिक ऑपरेशंस के लिए यह आवश्यक है ... और 'जेनेरिकऑन'/'शून्य' का उपयोग करके गणना करने के लिए अजीब है। – Daniel

+0

@Daniel - ठीक है, मुझे लगता है कि यह इस बात पर निर्भर करता है कि आपको कितना कुशल होना चाहिए। आपके प्रश्न में इस्तेमाल किए गए 'FromInt32' के अधिक सरल दृष्टिकोण के साथ कुछ भी गलत नहीं है, यह सिर्फ इतना है कि इसके परिणामस्वरूप अधिक ओवरहेड होगा। – kvb