c++ - सी++ 11 में लैम्ब्डा अभिव्यक्ति क्या है?




lambda c++11 (6)

सी ++ 11 में लैम्ब्डा अभिव्यक्ति क्या है? मैं कब उपयोग करूंगा? वे किस समस्या का समाधान करते हैं जो उनके परिचय से पहले संभव नहीं था?

कुछ उदाहरण, और मामलों का उपयोग उपयोगी होगा।


लैम्ब्डा फ़ंक्शन क्या है?

लैम्ब्डा फ़ंक्शन की सी ++ अवधारणा लैम्ब्डा कैलकुस और कार्यात्मक प्रोग्रामिंग में उत्पन्न होती है। एक लैम्ब्डा एक अनाम नाम है जो कोड के छोटे स्निपेट के लिए उपयोगी (वास्तविक प्रोग्रामिंग, सिद्धांत नहीं) है जो पुन: उपयोग करना असंभव है और नामकरण के लायक नहीं है।

सी ++ में एक लैम्ब्डा फ़ंक्शन इस तरह परिभाषित किया गया है

[]() { } // barebone lambda

या अपनी सारी महिमा में

[]() mutable -> T { } // T is the return type, still lacking throw()

[] कैप्चर सूची है, () तर्क सूची और {} फ़ंक्शन बॉडी।

कैप्चर सूची

कैप्चर सूची परिभाषित करती है कि लैम्ब्डा के बाहर से फ़ंक्शन बॉडी के अंदर और कैसे उपलब्ध होना चाहिए। यह या तो हो सकता है:

  1. एक मान: [x]
  2. एक संदर्भ [& x]
  3. संदर्भ में वर्तमान में किसी भी चर के दायरे में [&]
  4. 3 के समान, लेकिन मूल्य से [=]

आप किसी भी उपरोक्त कोमा से अलग सूची [x, &y] में मिश्रित कर सकते हैं।

तर्क सूची

तर्क सूची किसी भी अन्य C ++ फ़ंक्शन में समान है।

समारोह शरीर

कोड जिसे निष्पादित किया जाएगा जब लैम्ब्डा को वास्तव में बुलाया जाता है।

वापसी प्रकार कटौती

यदि लैम्ब्डा में केवल एक ही रिटर्न स्टेटमेंट है, तो वापसी का प्रकार छोड़ा जा सकता है और इसमें अंतर्निहित प्रकार की decltype(return_statement)

परिवर्तनशील

यदि एक लैम्ब्डा को म्यूटेबल चिह्नित किया जाता है (उदाहरण के लिए []() mutable { } ) इसे मान द्वारा कैप्चर किए गए मानों को म्यूट करने की अनुमति है।

बक्सों का इस्तेमाल करें

आईएसओ मानक द्वारा परिभाषित लाइब्रेरी लैम्बडास से भारी लाभ उठाती है और उपयोगिता को कई बार बढ़ाती है क्योंकि अब उपयोगकर्ताओं को कुछ सुलभ दायरे में छोटे मज़ेदारों के साथ अपने कोड को अव्यवस्थित करने की आवश्यकता नहीं है।

सी ++ 14

सी ++ 14 लैम्बडा में विभिन्न प्रस्तावों द्वारा विस्तारित किया गया है।

प्रारंभिक Lambda कैप्चर

कैप्चर सूची का एक तत्व अब = साथ शुरू किया जा सकता है। यह चर के नाम बदलने और आगे बढ़कर कैप्चर करने की अनुमति देता है। मानक से लिया गया एक उदाहरण:

int x = 4;
auto y = [&r = x, x = x+1]()->int {
            r += 2;
            return x+2;
         }();  // Updates ::x to 6, and initializes y to 7.

और विकिपीडिया से लिया गया यह दिखाता है कि std::move साथ कैप्चर कैसे करें:

auto ptr = std::make_unique<int>(10); // See below for std::make_unique
auto lambda = [ptr = std::move(ptr)] {return *ptr;};

जेनेरिक Lambdas

लैम्ब्डास अब सामान्य हो सकता है ( auto T बराबर होगा यदि T आसपास के दायरे में कहीं एक प्रकार का टेम्पलेट तर्क था):

auto lambda = [](auto x, auto y) {return x + y;};

बेहतर रिटर्न प्रकार कटौती

सी ++ 14 प्रत्येक फंक्शन के लिए रिटर्न प्रकारों को कम करने की अनुमति देता है और इसे फ़ॉर्म return expression; कार्यों तक सीमित नहीं करता है return expression; । यह भी lambdas तक बढ़ाया जाता है।


समस्या

सी ++ में उपयोगी सामान्य कार्य शामिल हैं जैसे std::for_each और std::transform , जो बहुत आसान हो सकता है। दुर्भाग्यवश वे भी उपयोग करने के लिए काफी बोझिल हो सकते हैं, खासकर यदि आप जिस functor को लागू करना चाहते हैं वह विशेष कार्य के लिए अद्वितीय है।

#include <algorithm>
#include <vector>

namespace {
  struct f {
    void operator()(int) {
      // do something
    }
  };
}

void func(std::vector<int>& v) {
  f f;
  std::for_each(v.begin(), v.end(), f);
}

यदि आप केवल एक बार एफ का उपयोग करते हैं और उस विशिष्ट स्थान पर ऐसा लगता है कि यह पूरी कक्षा को कुछ तुच्छ और एक बंद करने के लिए लिखता है।

सी ++ 03 में आप मज़ेदार स्थानीय को रखने के लिए निम्नलिखित की तरह कुछ लिखने के लिए प्रेरित हो सकते हैं:

void func2(std::vector<int>& v) {
  struct {
    void operator()(int) {
       // do something
    }
  } f;
  std::for_each(v.begin(), v.end(), f);
}

हालांकि इसकी अनुमति नहीं है, f को C ++ 03 में टेम्पलेट फ़ंक्शन में पास नहीं किया जा सकता है।

नया समाधान

सी ++ 11 लैम्बडास पेश करता है जो आपको struct f को बदलने के लिए एक इनलाइन, अज्ञात फ़ैक्टर लिखने की अनुमति देता है। छोटे सरल उदाहरणों के लिए यह पढ़ने के लिए क्लीनर हो सकता है (यह सब कुछ एक ही स्थान पर रखता है) और संभावित रूप से बनाए रखने के लिए सरल है, उदाहरण के लिए सबसे सरल रूप में:

void func3(std::vector<int>& v) {
  std::for_each(v.begin(), v.end(), [](int) { /* do something here*/ });
}

लैम्ब्डा फ़ंक्शन अज्ञात फ़ैक्टर के लिए सिंटैक्टिक चीनी हैं।

वापसी प्रकार

साधारण मामलों में लैम्बडा का रिटर्न प्रकार आपके लिए घटाया जाता है, उदाहरण के लिए:

void func4(std::vector<double>& v) {
  std::transform(v.begin(), v.end(), v.begin(),
                 [](double d) { return d < 0.00001 ? 0 : d; }
                 );
}

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

void func4(std::vector<double>& v) {
    std::transform(v.begin(), v.end(), v.begin(),
        [](double d) {
            if (d < 0.0001) {
                return 0;
            } else {
                return d;
            }
        });
}

इसे हल करने के लिए आपको -> T लैम्बडा फ़ंक्शन के लिए रिटर्न प्रकार को स्पष्ट रूप से निर्दिष्ट करने की अनुमति है -> T :

void func4(std::vector<double>& v) {
    std::transform(v.begin(), v.end(), v.begin(),
        [](double d) -> double {
            if (d < 0.0001) {
                return 0;
            } else {
                return d;
            }
        });
}

चर "कैप्चरिंग" चर

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

void func5(std::vector<double>& v, const double& epsilon) {
    std::transform(v.begin(), v.end(), v.begin(),
        [epsilon](double d) -> double {
            if (d < epsilon) {
                return 0;
            } else {
                return d;
            }
        });
}

आप संदर्भ और मूल्य दोनों द्वारा कैप्चर कर सकते हैं, जिसे आप क्रमशः & and = का उपयोग करके निर्दिष्ट कर सकते हैं:

  • [&epsilon] संदर्भ द्वारा कब्जा
  • [&] संदर्भ द्वारा लैम्ब्डा में उपयोग किए जाने वाले सभी चर को कैप्चर करता है
  • [=] लैम्बडा में मूल्य के द्वारा उपयोग किए जाने वाले सभी चर को कैप्चर करता है
  • [&, epsilon] वैरिएबल को कैप्चर करता है जैसे [&], लेकिन मूल्य से ईपीएसलॉन
  • [=, &epsilon] [=] के साथ वैरिएबल कैप्चर करता है, लेकिन संदर्भ द्वारा epsilon

जनरेटेड operator() डिफ़ॉल्ट रूप से const होता है, निहितार्थ के साथ कि जब आप डिफ़ॉल्ट रूप से उन्हें एक्सेस करते हैं तो कैप्चर होगा। इसका प्रभाव यह है कि एक ही इनपुट के साथ प्रत्येक कॉल एक ही परिणाम उत्पन्न करेगा, हालांकि आप लैम्बडा को mutable रूप में चिह्नित कर सकते हैं ताकि अनुरोध किया जा सके कि operator() जो उत्पादित है, वह const नहीं है।


एक समस्या यह हल करती है: कन्स्ट्रक्टर में कॉल के लिए लैम्ब्डा से कोड सरल है जो एक कॉन्स्ट सदस्य प्रारंभ करने के लिए आउटपुट पैरामीटर फ़ंक्शन का उपयोग करता है

आप अपने वर्ग के एक कॉन्स सदस्य को एक फ़ंक्शन पर कॉल के साथ प्रारंभ कर सकते हैं जो आउटपुट पैरामीटर के रूप में अपना आउटपुट वापस देकर अपना मान निर्धारित करता है।


खैर, मैंने पाया है कि एक व्यावहारिक उपयोग बॉयलर प्लेट कोड को कम कर रहा है। उदाहरण के लिए:

void process_z_vec(vector<int>& vec)
{
  auto print_2d = [](const vector<int>& board, int bsize)
  {
    for(int i = 0; i<bsize; i++)
    {
      for(int j=0; j<bsize; j++)
      {
        cout << board[bsize*i+j] << " ";
      }
      cout << "\n";
    }
  };
  // Do sth with the vec.
  print_2d(vec,x_size);
  // Do sth else with the vec.
  print_2d(vec,y_size);
  //... 
}

लैम्ब्डा के बिना, आपको विभिन्न bsize मामलों के लिए कुछ करने की आवश्यकता हो सकती है। बेशक आप एक फ़ंक्शन बना सकते हैं लेकिन यदि आप आत्मा उपयोगकर्ता फ़ंक्शन के दायरे में उपयोग को सीमित करना चाहते हैं तो क्या होगा? लैम्ब्डा की प्रकृति इस आवश्यकता को पूरा करती है और मैं उस मामले के लिए इसका उपयोग करता हूं।


lambda expression का सबसे अच्छा स्पष्टीकरण सी ++ बजेर्न स्ट्राउस्ट्रप के लेखक से उनकी पुस्तक ***The C++ Programming Language*** अध्याय 11 ( आईएसबीएन -13: 978-0321563842 ) में दिया गया है:

What is a lambda expression?

एक लैम्ब्डा अभिव्यक्ति , जिसे कभी-कभी लैम्ब्डा फ़ंक्शन के रूप में भी जाना जाता है या ( लैम्ब्डा के रूप में सख्ती से गलत तरीके से बोलते हुए, लेकिन बोलचाल से) कहा जाता है, एक अज्ञात फ़ंक्शन ऑब्जेक्ट को परिभाषित करने और उपयोग करने के लिए एक सरल संकेत है। एक ऑपरेटर () के साथ नामित वर्ग को परिभाषित करने के बजाय, बाद में उस वर्ग का एक ऑब्जेक्ट बनाना, और अंत में इसे आमंत्रित करना, हम एक शॉर्टेंड का उपयोग कर सकते हैं।

When would I use one?

यह विशेष रूप से उपयोगी होता है जब हम एक ऑपरेशन को एल्गोरिदम के लिए तर्क के रूप में पास करना चाहते हैं। ग्राफ़िकल यूजर इंटरफेस (और कहीं और) के संदर्भ में, ऐसे ऑपरेशन को अक्सर कॉलबैक के रूप में जाना जाता है।

What class of problem do they solve that wasn't possible prior to their introduction?

यहां मुझे लगता है कि लैम्ब्डा अभिव्यक्ति के साथ किए गए हर क्रिया को उनके बिना हल किया जा सकता है, लेकिन अधिक कोड और बहुत बड़ी जटिलता के साथ। Lambda अभिव्यक्ति यह आपके कोड के लिए अनुकूलन का तरीका है और इसे और अधिक आकर्षक बनाने का एक तरीका है। Stroustup द्वारा उदास के रूप में:

अनुकूलित करने के प्रभावी तरीके

Some examples

लैम्ब्डा अभिव्यक्ति के माध्यम से

void print_modulo(const vector<int>& v, ostream& os, int m) // output v[i] to os if v[i]%m==0
{
    for_each(begin(v),end(v),
        [&os,m](int x) { 
           if (x%m==0) os << x << '\n';
         });
}

या समारोह के माध्यम से

class Modulo_print {
         ostream& os; // members to hold the capture list int m;
     public:
         Modulo_print(ostream& s, int mm) :os(s), m(mm) {} 
         void operator()(int x) const
           { 
             if (x%m==0) os << x << '\n'; 
           }
};

या और भी

void print_modulo(const vector<int>& v, ostream& os, int m) 
     // output v[i] to os if v[i]%m==0
{
    class Modulo_print {
        ostream& os; // members to hold the capture list
        int m; 
        public:
           Modulo_print (ostream& s, int mm) :os(s), m(mm) {}
           void operator()(int x) const
           { 
               if (x%m==0) os << x << '\n';
           }
     };
     for_each(begin(v),end(v),Modulo_print{os,m}); 
}

यदि आपको आवश्यकता है तो आप नीचे lambda expression नाम दे सकते हैं:

void print_modulo(const vector<int>& v, ostream& os, int m)
    // output v[i] to os if v[i]%m==0
{
      auto Modulo_print = [&os,m] (int x) { if (x%m==0) os << x << '\n'; };
      for_each(begin(v),end(v),Modulo_print);
 }

या एक और सरल नमूना मानो

void TestFunctions::simpleLambda() {
    bool sensitive = true;
    std::vector<int> v = std::vector<int>({1,33,3,4,5,6,7});

    sort(v.begin(),v.end(),
         [sensitive](int x, int y) {
             printf("\n%i\n",  x < y);
             return sensitive ? x < y : abs(x) < abs(y);
         });


    printf("sorted");
    for_each(v.begin(), v.end(),
             [](int x) {
                 printf("x - %i;", x);
             }
             );
}

अगले उत्पन्न होगा

0

1

0

1

0

1

0

1

0

1

0 सॉर्टेडएक्स - 1; एक्स - 3; एक्स - 4; एक्स - 5; एक्स - 6; एक्स - 7; एक्स - 33;

[] - यह कैप्चर सूची या lambda introducer : यदि lambda introducer को अपने स्थानीय पर्यावरण तक पहुंच की आवश्यकता नहीं है तो हम इसका उपयोग कर सकते हैं।

पुस्तक से उद्धरण:

लैम्ब्डा अभिव्यक्ति का पहला अक्षर हमेशा होता है [ । एक लैम्ब्डा परिचयकर्ता विभिन्न रूप ले सकता है:

[] : एक खाली कैप्चर सूची। इसका तात्पर्य है कि आसपास के संदर्भ से स्थानीय नाम लैम्बडा शरीर में उपयोग नहीं किए जा सकते हैं। ऐसे लैम्ब्डा अभिव्यक्तियों के लिए, डेटा तर्क से या nonlocal चर से प्राप्त किया जाता है।

[&] : संदर्भ द्वारा पूरी तरह से कब्जा। सभी स्थानीय नामों का उपयोग किया जा सकता है। संदर्भ के अनुसार सभी स्थानीय चर का उपयोग किया जाता है।

[=] : मूल्य से पूरी तरह से कब्जा। सभी स्थानीय नामों का उपयोग किया जा सकता है। सभी नाम लैम्ब्डा अभिव्यक्ति के कॉल के बिंदु पर उठाए गए स्थानीय चर की प्रतियों को संदर्भित करते हैं।

[कैप्चर-लिस्ट]: स्पष्ट कैप्चर; कैप्चर-लिस्ट संदर्भित या मूल्य द्वारा कब्जा करने के लिए स्थानीय चर के नामों की सूची (यानी वस्तु में संग्रहीत) की सूची है। संदर्भ के साथ वैरिएबल और संदर्भ द्वारा कब्जा कर लिया गया है। अन्य चर मूल्य से कब्जा कर लिया जाता है। एक कैप्चर सूची में तत्व और तत्वों के रूप में भी शामिल हो सकते हैं।

[&, कैप्चर-लिस्ट] : संदर्भ में सभी स्थानीय चरों के संदर्भ में स्पष्ट रूप से कैप्चर करें, जो सूची में पुरुषों से मेल नहीं खाते हैं। कैप्चर सूची में यह शामिल हो सकता है। सूचीबद्ध नामों से पहले नहीं किया जा सकता है। कैप्चर सूची में नामित चर वैल्यू द्वारा कैप्चर किए जाते हैं।

[=, कैप्चर-लिस्ट] : सूची में उल्लिखित नामों के साथ सभी स्थानीय चर के मूल्य से पूर्ण रूप से कैप्चर करें। कैप्चर सूची में यह शामिल नहीं हो सकता है। सूचीबद्ध नाम पहले से पहले होना चाहिए। कैप्चर सूची में नामित चर-एबल्स संदर्भ द्वारा कब्जा कर लिया जाता है।

ध्यान दें कि एक स्थानीय नाम पहले से संदर्भित होता है और हमेशा संदर्भ द्वारा कब्जा कर लिया जाता है और स्थानीय नाम द्वारा पूर्ववत नहीं किया जाता है और हमेशा मूल्य से कब्जा कर लिया जाता है। संदर्भ द्वारा केवल कैप्चरिंग कॉलिंग वातावरण में चर के संशोधन की अनुमति देता है।

Additional

Lambda expression प्रारूप

अतिरिक्त संदर्भ:


जवाब

प्रश्न: सी ++ 11 में लैम्ब्डा अभिव्यक्ति क्या है?

ए: हुड के तहत यह ओवरलोडिंग क्लास ( ऑब्जेक्ट कॉन्स ) के साथ स्वत: जेनरेटेड क्लास का ऑब्जेक्ट है। इस तरह की वस्तु को क्लोजर द्वारा बंद और बनाया जाता है। यह 'बंद' अवधारणा सी ++ 11 से बाइंड अवधारणा के करीब है। लेकिन लैम्ब्डा आमतौर पर बेहतर कोड उत्पन्न करते हैं। और बंद करने के माध्यम से कॉल पूर्ण इनलाइनिंग की अनुमति देता है।

प्रश्न: मैं कब उपयोग करूंगा?

ए: "सरल और छोटे तर्क" को परिभाषित करने के लिए और पिछले प्रश्न से संकलक प्रदर्शन पीढ़ी से पूछें। आप एक संकलक कुछ अभिव्यक्ति देते हैं जो आप ऑपरेटर () के अंदर होना चाहते हैं। अन्य सभी सामान संकलक आपको उत्पन्न करेंगे।

प्रश्न: वे किस समस्या का समाधान करते हैं जो उनके परिचय से पहले संभव नहीं था?

ए: यह कुछ प्रकार की सिंटैक्स चीनी है जैसे कि कस्टम एड, सबट्रैक्ट ऑपरेशंस के लिए फ़ंक्शंस के बजाय ऑपरेटर ओवरलोडिंग ... लेकिन यह अनियंत्रित कोड की अधिक पंक्तियों को सहेजने के लिए कुछ कक्षाओं और वास्तविक आदि के वास्तविक तर्क की 1-3 लाइनों को लपेटने के लिए बचाता है! कुछ इंजीनियरों का मानना ​​है कि यदि लाइनों की संख्या कम है तो इसमें त्रुटियों को कम करने का एक कम मौका है (मुझे ऐसा भी लगता है)

उपयोग का उदाहरण

auto x = [=](int arg1){printf("%i", arg1); };
void(*f)(int) = x;
f(1);
x(1);

लैम्बदास के बारे में अतिरिक्त, प्रश्न से ढंके नहीं। यदि आप रुचि नहीं रखते हैं तो इस खंड को अनदेखा करें

1. कैप्चर मूल्य। आप कैप्चर करने के लिए क्या कर सकते हैं

1.1। आप लैमडास में स्थिर भंडारण अवधि के साथ चर के संदर्भ में संदर्भित कर सकते हैं। वे सब कब्जा कर लिया गया है।

1.2। आप "मूल्य से" मूल्यों को कैप्चर करने के लिए लैम्डा का उपयोग कर सकते हैं। ऐसे मामले में कैप्चर वर्र्स को फंक्शन ऑब्जेक्ट (क्लोजर) में कॉपी किया जाएगा।

[captureVar1,captureVar2](int arg1){}

1.3। आप संदर्भ हो सकते हैं। & - इस संदर्भ में संदर्भ संदर्भ, पॉइंटर्स नहीं।

   [&captureVar1,&captureVar2](int arg1){}

1.4। यह सभी गैर स्थैतिक वर्रों को मूल्य, या संदर्भ द्वारा कैप्चर करने के लिए नोटेशन मौजूद है

  [=](int arg1){} // capture all not-static vars by value

  [&](int arg1){} // capture all not-static vars by reference

1.5। यह मान द्वारा सभी गैर स्थैतिक वार्स को कैप्चर करने के लिए, या संदर्भ द्वारा और smth निर्दिष्ट करने के लिए मौजूद है। अधिक। उदाहरण: मूल्य से सभी गैर-स्थैतिक युद्धों को कैप्चर करें, लेकिन संदर्भ द्वारा Param2 पर कब्जा करें

[=,&Param2](int arg1){} 

संदर्भ द्वारा सभी गैर-स्थैतिक युद्धों को कैप्चर करें, लेकिन मान कैप्चर पैराम 2 द्वारा

[&,Param2](int arg1){} 

2. वापसी प्रकार कटौती

2.1। अगर लम्दा एक अभिव्यक्ति है तो लैम्ब्डा रिटर्न प्रकार को घटाया जा सकता है। या आप इसे स्पष्ट रूप से निर्दिष्ट कर सकते हैं।

[=](int arg1)->trailing_return_type{return trailing_return_type();}

यदि लैम्ब्डा में एक अभिव्यक्ति है तो, वापसी प्रकार को पिछला रिटर्न प्रकार के माध्यम से निर्दिष्ट किया जाना चाहिए। ऑटो फ़ंक्शंस और सदस्य-फ़ंक्शंस पर भी समान वाक्यविन्यास लागू किया जा सकता है

3. कैप्चर किए गए मान। आप कैप्चर नहीं कर सकते हैं

3.1। आप केवल स्थानीय वर्र्स को कैप्चर कर सकते हैं, ऑब्जेक्ट के सदस्य चर नहीं।

4. Сonversions

4.1। लैम्ब्डा एक फ़ंक्शन पॉइंटर नहीं है और यह एक अनाम कार्य नहीं है , लेकिन इसे निश्चित रूप से फ़ंक्शन पॉइंटर में परिवर्तित किया जा सकता है।

ps

  1. लैम्ब्डा व्याकरण जानकारी के बारे में अधिक जानकारी प्रोग्रामिंग भाषा सी ++ # 337, 2012-01-16, 5.1.2 के लिए वर्किंग ड्राफ्ट में पाई जा सकती है। लैम्ब्डा अभिव्यक्तियां, पृष्ठ 8.8

  2. सी ++ 14 में अतिरिक्त सुविधा जिसे "इनिट कैप्चर" के रूप में नामित किया गया है, जोड़ा गया है। यह क्लोजर डेटा सदस्यों की मनमाने ढंग से घोषणा करने की अनुमति देता है:

    auto toFloat = [](int value) { return float(value);};
    auto interpolate = [min = toFloat(0), max = toFloat(255)](int value)->float { return (value - min) / (max - min);};
    




c++-faq