سؤال

من قبل "غير قابل للتغيير وظيفة" أو "غير قابل للتغيير الأسلوب" ، أعني وظيفة الذين النتيجة لن تختلف إذا كنت تعطيه نفس الحجج.

وأود أن تكون مهتمة لمعرفة إذا كان أي شخص يعرف أكثر من عام أو أقل مطول الحل عندما تريد تخزين precomputed قيمة(s) من وظيفة ثابتة.

اسمحوا لي أن أشرح ماذا أقصد بمثال بسيط:

//Let's assume that ComputeStuff() returns a widely used value 
//and that 
//1. It is immutable (it will always return the same result)
//2. its performance is critical, and it cannot be accepted to compute
//   the result at each call, because the computation is too slow
//I show here a way to solve the problem, based on a cached result.
//(this example works in a case of a method with no arguments. 
// A hash would be required in order to store multiple precomputed results 
//depending upon the arguments)
private string mComputeStuff_Cached = null;
public string ComputeStuff()
{
  if (mComputeStuff_Cached != null)
    return mComputeStuff_Cached ;

  string result;
  //
  // ...
  //Do lots of cpu intensive computation in order to compute "result" 
  //or whatever you want to compute
  //(for example the hash of a long file)
  //...
  //

  mComputeStuff_Cached  = result;
  return mComputeStuff_Cached ;
}

ملاحظات:
- أضفت الوسم C++ كما حل في C++ أيضا مصلحة لي
- مفهوم "غير قابل للتغيير وظائف" الشائع قاعدة بيانات المطورين منذ وظيفة يمكن تعريفها بأنها "غير قابل للتغيير" ، أو "غير قابل للتغيير في المعاملات" (هذا هو وسيلة جيدة لتحسين أداء الاستعلامات).

شكرا مقدما

هل كانت مفيدة؟

المحلول

و" التحفيظ " قد يكون مفيدا على المدى، هنا. هناك عدد قليل من المكتبات التحفيظ هناك (أكاد أقسم كان هناك واحد في rel="nofollow تعزيز ، ولكن لا أستطيع العثور عليها في الوقت الحاضر). سوف يقوم بعمل بحث عن "memoize" أو "التحفيظ" واللغة التي تختارها تكشف عن وجود عدد قليل من الزيارات.

وهنا مقال أنيق في ويكي الكتب: تحسين ++ / C تقنيات التحسين العامة / التحفيظ

نصائح أخرى

حسنا، وذلك باستخدام مندوب مثل Func<T> يمكن جعلها أكثر إعادة صالحة للاستعمال، دون الحاجة إلى تعدد الأشكال / الميراث - ولكن ليس هناك شيء أكثر "يحمل في ثناياه عوامل" في C #:

using System;
static class Program {
    static void Main() {
        var func = CachedFunc.Create(() => int.Parse(Console.ReadLine()));

        Console.WriteLine(func.Value);
        Console.WriteLine(func.Value);
    }
}
static class CachedFunc {
    public static CachedFunc<T> Create<T>(Func<T> func) {
        return new CachedFunc<T>(func);
    }
}
class CachedFunc<T> {
    T value;
    Func<T> func;
    public CachedFunc(Func<T> func){
        if (func == null) throw new ArgumentNullException("func");
        this.func = func;
    }
    public T Value {
        get {
            if (func != null) {
                value = func();
                func = null;
            }
            return value;
        }
    }
    public static explicit operator T(CachedFunc<T> func) {
        return func.Value; }
}

هل يمكن أن تجرب شيئا من هذا القبيل:

#include <functional>
#include <type_traits>
#include <map>
#include <tuple>

//requires c++14
auto add_function_cache = [](auto fun) {
    using fun_type = decltype(fun);
    return ([=](auto... run_args){
        using fun_return_type = std::result_of_t<fun_type(decltype(run_args)...)>;
        static std::map<
            std::tuple<decltype(run_args)...>,
            fun_return_type
        > result_cache;
        std::tuple<decltype(run_args)...> tuple(run_args...);
        if (result_cache.find(tuple) == result_cache.end()) {
            fun_return_type rv = fun(run_args...);
            result_cache[tuple] = rv; 
            return rv; 
        }   
        else {
            return result_cache[tuple];
        }   
    }); 
};

template <typename R, class... Args> auto
add_function_cache_old(std::function<R(Args...)> fun)
-> std::function<R(Args...)>
{
    std::map<std::tuple<Args...>, R> cache;
    return [=](Args... args) mutable  {
        std::tuple<Args...> t(args...);
        if (cache.find(t) == cache.end()) {
            R rv = fun(args...);
            cache[t] = rv; 
            return rv; 
        }   
        else {
            return cache[t];
        }   
    };  
};

وثم استخدامه كما يلي:

//function_cache - usage
auto fib_cache = add_function_cache(&fib);

//function_cache_old - usage
function<decltype(fib)> fib_fn = &fib;
auto fib_cache_old = add_function_cache_old(fib_fn);

fib_cache(10);
fib_cache(10);

والفكرة هي إنشاء دالة التي تأخذ وظيفة (متعة)، وحجة ويعود آخر وظيفة. وظيفة عاد يلتف متعة وتوفر خريطة المعلمات شكل الإدخال (run_args) إلى نتائج. لذلك لديه نفس التوقيع من المرح، إطارات للعثور على نتيجة لمعلمات معينة (run_args) في خارطة (مخبأ). ثم إرجاع القيمة المخزنة مؤقتا أو نتيجة تحسب المرح. من الواضح والتي يمكن ان تضاف نتيجة متعة لذاكرة التخزين المؤقت في حالة غير ناجحة نظرة المتابعة.

والتحيات

وكان

يمكنك أن تجعل أقل قليلا مطول:

private string mComputeStuff_Cached = null;
public string ComputeStuff()
{
  if (mComputeStuff_Cached == null) {
    string result;
    //
    // ...
    //Do lots of cpu intensive computation in order to compute "result" 
    //or whatever you want to compute
    //(for example the hash of a long file)
    //...
    //

    mComputeStuff_Cached  = result;
  }

  return mComputeStuff_Cached ;
}

ملاحظات أخرى على هذا النوع من نمط:

  • إذا كنت تستخدم C++, و مثل هذا الأسلوب const, ثم تعديل الأعضاء لن يكون ممكنا لأنهم أيضا معاملة const في سياق هذا الأسلوب.ومع ذلك ، يمكنك تجاوز هذا بمناسبة عضو(s) تحتاج إلى تعديل كما mutable.
  • هناك فرق بين "الدلالي const" و "المعامل const".عندما مؤلف علامات شيء const, أنهم عادة ما يعني "الدلالي const" (وهو ما يعني في هذه الحالة) لكن المترجم لا يمكن إلا فرض "أحادي المعامل const".
  • const طرق عادة ما تعطي المتصل الانطباع بأنهم يمكن أن يسمى من مواضيع متعددة في وقت واحد ، لأن لديهم أي آثار جانبية.في هذا النوع من نمط لديك "المعامل" تأثير جانبي ولكن لا "الدلالي" الآثار الجانبية.ولذلك إذا كان من الممكن أن مؤشرات ترابط متعددة يمكن أن نسمي مثل هذا الأسلوب يجب داخليا حماية هذه التهيئة.

ويمكنك ان تجعل عضويتك قابلة للتغيير متغير (الكلمة).

وهذا سيسمح دالة عضو CONST تعديل هذه القيمة. أنا استخدامها في كل وقت لتخزين نتائج المتوسطة.

ويمكنك إعادة كتابة هذا الرمز حرفيا تقريبا في C ++

class CClass
{

  private:
     std::string* mComputeStuff_Cached;

  public:
     CClass()
       mComputeStuff_Cached(NULL)
     {

     }

     ~CClass()
     {
           delete mComputeStuff_Cached;
     }


     std::string ComputeStuff()
     {
         if (mComputeStuff_Cached != NULL)
         {
             return mComputeStuff_Cached
         }
         else
         {
             std::string calcedAnswer;
             ...
             // store away answer
             mComputeStuff_Cached = new std::string(calcedAnswer);
         }
     }
};

ولست متأكدا إذا كان الاختيار لمعرفة ما إذا mComputeStuff_Cached فارغ () غير كافية. ويمكن أن يكون فارغا () هو نتيجة مؤقتا مشروعة.

ويمكن أن تستخدم أنت الكلمة static داخل الدالة. سيتم احتساب ذلك مرة واحدة فقط:

std::string GetWidelyUsedValue()
{
   static std::string value = ComputeStuff() ;
   return value ;
}

std::string ComputeStuff()
{
   // Compute the string here.
}

ومن شأن C ++ حل تكون مشابهة جدا لما كنت قد المذكورة، واختلاف فقط في مزيج مسكر من const اجهة المؤهلة العامة (ق) و (بعض) أعضاء mutable:

class Computer {
    mutable string cache;
  public:
    // I wouldn't call it ComputeXXX
    // since I want to hide the implementation
    // details from my client: for the client
    // there is no change in state due to a call
    // to this function
    const string& StringVal() const {
         if (cache.empty()) {
                // compute cache
         }
         return cache;
    }
    // ...
};              
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top