प्रक्रियात्मक पैरामीटर

From Vigyanwiki

कम्प्यूटिंग में, प्रक्रियात्मक पैरामीटर प्रक्रिया का एक पैरामीटर होता है जो स्वयं एक प्रक्रिया होती है।

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

यह उपकरण उन लैंग्वेज में विशेष रूप से प्रभावी और सुविधाजनक है जो लोकल फ़ंक्शन परिभाषाओं जैसे पास्कल और C की आधुनिक जीएनयू लैंग्वेज का समर्थन करते हैं। यह तब और भी अधिक होता है जब फ़ंक्शन क्लोजर उपलब्ध होते हैं। ऑब्जेक्ट ओरिएंटेड प्रोग्रामिंग लैंग्वेज में ऑब्जेक्ट द्वारा समान कार्यक्षमता (और अधिक) लेकिन काफी अधिक कीमत पर प्रदान की जाती है।

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

मूल अवधारणा

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

procedure P(f):
    return f(6,3) * f(2,1)

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

procedure plus(x, y):
    return x + y

तो हम P (plus) कह सकते हैं, और परिणाम plus(6,3) * plus(2,1) = (6 + 3)*(2 + 1) = 27 होगा। दूसरी ओर, यदि हम परिभाषित करते हैं।

procedure quot(u, v):
    return u/v

फिर P (quot) को कॉल करने पर quot(6,3)*quot(2,1) = (6/3)*(2/1) = 4 आएगा। अंत में, यदि हम परिभाषित करते हैं

procedure evil(z)
    return z + 100

तब कॉल P (evil) का कोई विशेष अर्थ नहीं होगा, और इसे एक त्रुटि के रूप में चिह्नित किया जा सकता है।

वाक्यविन्यास विवरण

कुछ प्रोग्रामिंग भाषाएं जिनमें यह सुविधा है, वे प्रत्येक प्रक्रियात्मक पैरामीटर f के लिए एक पूर्ण प्रकार की घोषणा की अनुमति दे सकती हैं या इसकी आवश्यकता हो सकती है, जिसमें इसके तर्कों की संख्या और प्रकार और इसके परिणाम का प्रकार, यदि कोई हो, सम्मिलित है। उदाहरण के लिए, C प्रोग्रामिंग लैंग्वेज में, ऊपर दिए गए उदाहरण को इस प्रकार लिखा जा सकता है

int P(int (*f)(int a, int b)) {
    return f(6,3) * f(2,1);
}

सिद्धांत रूप में, वास्तविक फ़ंक्शन actf जिसे P को कॉल करने पर तर्क के रूप में पारित किया जाता है, प्रक्रिया पैरामीटर f के घोषित प्रकार के साथ टाइप-संगत होना चाहिए। इसका सामान्यतः मतलब यह है कि actf और f को एक ही प्रकार का परिणाम देना चाहिए, समान संख्या में तर्क होने चाहिए, और संबंधित तर्कों का प्रकार भी समान होना चाहिए। हालाँकि, तर्कों के नाम समान होने की आवश्यकता नहीं है, जैसा कि ऊपर दिए गए प्लस और उद्धरण उदाहरणों से एड्रेस चलता है। हालाँकि, इस संबंध में कुछ प्रोग्रामिंग भाषाएँ अधिक प्रतिबंधात्मक या अधिक उदार हो सकती हैं।

स्कोपिंग

ऐसी लैंग्वेज में जो प्रक्रियात्मक पैरामीटर की अनुमति देती हैं, स्कोपिंग नियमों को सामान्यतः इस तरह से परिभाषित किया जाता है कि प्रक्रियात्मक पैरामीटर उनके मूल दायरे में निष्पादित होते हैं। अधिक सटीक रूप से, मान लीजिए कि फ़ंक्शन actf को पी के तर्क के रूप में, इसके प्रक्रियात्मक पैरामीटर f के रूप में पारित किया जाता है; और फिर P के बॉडी के अंदर से f कॉल किया जाता है जबकि actf निष्पादित किया जा रहा है, यह इसकी परिभाषा के वातावरण को देखता है।

इन दायरे नियमों का कार्यान्वयन साधारण नहीं है। जब तक actf अंततः निष्पादित होता है, तब तक सक्रियण रिकॉर्ड जहां इसके पर्यावरण चर रहते हैं, स्टैक में अनियमित ढंग से गहरे हो सकते हैं। यह तथाकथित डाउनवर्ड फ़नर्ग समस्या है।

उदाहरण: जेनेरिक इंसर्शन सॉर्ट

प्रक्रियात्मक पैरामीटर की अवधारणा को उदाहरणों द्वारा सर्वोत्तम रूप से समझाया गया है। एक विशिष्ट एप्लिकेशन इंसर्शन सॉर्ट एल्गोरिदम का निम्नलिखित सामान्य कार्यान्वयन है, जो दो पूर्णांक पैरामीटर a, b और दो प्रक्रियात्मक पैरामीटर prec, स्वैप लेता है:

procedure isort(a, b, prec, swap):
    integer i, j;
    i  a;
    while i  b do
        j  i;
        while j > a and prec(j, j1) do
            swap(j, j1);
            j  j1;
        i  i+1;

इस प्रक्रिया का उपयोग उपयोगकर्ता द्वारा निर्दिष्ट क्रम में मनमाने प्रकार के कुछ ऐरे x के x[a] से x[b] तक एलिमेंट को क्रमबद्ध करने के लिए किया जा सकता है। पैरामीटर prec और स्वैप दो फ़ंक्शन होने चाहिए, जो क्लाइंट द्वारा परिभाषित किए गए हैं, दोनों a और b के बीच दो पूर्णांक r, s लेते हैं। prec फ़ंक्शन को सत्य लौटना चाहिए यदि और केवल तभी जब क्लाइंट द्वारा परिभाषित क्रम में x[r] में संग्रहीत डेटा x[s] में संग्रहीत डेटा से पहले होना चाहिए। स्वैप फ़ंक्शन को x[r] और x[s] की सामग्री का आदान-प्रदान करना चाहिए, और कोई परिणाम नहीं लौटाना चाहिए।

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

फ़्लोटिंग-पॉइंट नंबरों को क्रमबद्ध करना

उदाहरण के लिए, हम isort (1, 20,zprec,zswap) को कॉल करके बढ़ते क्रम में 20 फ़्लोटिंग-पॉइंट नंबरों, z[1] से z[20] की एक ऐरे z को सॉर्ट कर सकते हैं, जहां फ़ंक्शन zprec और zswap को इस प्रकार परिभाषित किया गया है

procedure zprec(r, s):
    return (z[r] < z[s]);

procedure zswap(r, s):
    float t;
    t  z[r];
    z[r]  z[s];
    z[s]  t

मैट्रिक्स की पंक्तियों को क्रमबद्ध करना

एक अन्य उदाहरण के लिए, मान लीजिए कि M 10 पंक्तियों और 20 स्तंभों के साथ पूर्णांकों का मैट्रिक्स है, जिसका सूचकांक 1 से प्रारम्भ होता है। निम्नलिखित कोड प्रत्येक पंक्ति में एलिमेंट को पुनर्व्यवस्थित करेगा ताकि सभी सम मान सभी विषम मानों से पहले आएं:

integer i
procedure eoprec(r, s):
    return (M[i, r] mod 2) < (M[i, s] mod 2);

procedure eoswap(r, s):
    integer t;
    t  M[i,r];
    M[i,r]  M[i,s];
    M[i,s]  t;

for i from 1 to 10 do
    isort(1, 20, eoprec, eoswap);

ध्यान दें कि eoprec और eoswap का प्रभाव पंक्ति संख्या i पर निर्भर करता है, लेकिन isort प्रक्रिया को यह जानने की आवश्यकता नहीं है।

वेक्टर-सॉर्टिंग प्रक्रिया

निम्नलिखित उदाहरण प्रक्रिया vecsort को परिभाषित करने के लिए isort का उपयोग करता है जो एलिमेंट v[0] से v[n−1] तक पूर्णांक n और पूर्णांक वेक्टर v लेता है और उन्हें बढ़ते या घटते क्रम में क्रमबद्ध करता है,  यह इस बात पर निर्भर करता है कि तीसरा पैरामीटर incr क्रमशः true है या false:

procedure vecsort(n, v, incr):

    procedure vprec(r, s):
        if incr then
            return v[r] < v[s];
        else
            return v[r] > v[s];

    procedure vswap(r, s):
        integer t;
        t  v[r];
        v[r]  v[s];
        v[s]  t

    isort(0, n1, vprec, vswap);

फ़ंक्शन vprec प्राप्त करने के लिए नेस्टेड फ़ंक्शन परिभाषाओं के उपयोग पर ध्यान दें जिसका प्रभाव vecsort को दिए गए पैरामीटर incr पर निर्भर करता है। ऐसी भाषाओं में जो मानक C जैसी नेस्टेड फ़ंक्शन परिभाषाओं की अनुमति नहीं देती हैं, इस प्रभाव को प्राप्त करने के लिए जटिल और/या थ्रेड-असुरक्षित कोड की आवश्यकता होगी।

उदाहरण: दो अनुक्रमों को मर्ज करना

निम्नलिखित उदाहरण अमूर्त डेटा संरचनाओं को उनके ठोस कार्यान्वयन से स्वतंत्र रूप से संसाधित करने के लिए प्रक्रियात्मक पैरामीटर के उपयोग को दर्शाता है। समस्या रिकॉर्ड के दो क्रमबद्ध अनुक्रमों को एकल क्रमबद्ध अनुक्रम में विलय करने की है, जहां रिकॉर्ड की प्रकृति और ऑर्डरिंग पैरामीटर क्लाइंट द्वारा चुना जा सकता है। निम्नलिखित कार्यान्वयन केवल यह मानता है कि प्रत्येक रिकॉर्ड को मेमोरी एड्रेस द्वारा संदर्भित किया जा सकता है, और "नल एड्रेस" Λ है जो किसी भी वैध रिकॉर्ड का एड्रेस नहीं है। क्लाइंट को प्रत्येक क्रम में पहले रिकॉर्ड के A, B एड्रेस और बाद में वर्णित किए जाने वाले फ़ंक्शन prec, next और append जिसका वर्णन बाद में किया जाएगा।

procedure merge(A, B, prec, nextA, appendA, nextB, appendB):
    address ini, fin, t
    ini  Λ; fin  Λ
    while A  Λ or B  Λ do
        if B = Λ or (A  Λ and B  Λ and prec(A, B)) then
            t  nextA(A)
            fin  appendA(A, fin); if ini = Λ then ini  fin
            A  t
        else
            t  nextB(B)
            fin  appendB(B, fin); if ini = Λ then ini  fin
            B  t
    return ini

फ़ंक्शन prec को दो रिकॉर्ड्स के एड्रेस r, s लेना चाहिए, प्रत्येक अनुक्रम से एक, और यदि पहला रिकॉर्ड आउटपुट अनुक्रम में दूसरे से पहले आना चाहिए तो सही लौटना चाहिए। फ़ंक्शन nextA को पहले अनुक्रम से एक रिकॉर्ड का एड्रेस लेना चाहिए, और उसी क्रम में अगले रिकॉर्ड का एड्रेस लौटाना चाहिए, या यदि कोई नहीं है तो Λ लौटाना चाहिए। फ़ंक्शन appendA को अनुक्रम A से आउटपुट अनुक्रम में पहला रिकॉर्ड जोड़ना चाहिए; इसके तर्क जोड़े जाने वाले रिकॉर्ड का एड्रेस A और आउटपुट सूची के अंतिम रिकॉर्ड का एड्रेस fin (या यदि वह सूची अभी भी खाली है) हैं। प्रक्रिया परिशिष्ट को आउटपुट सूची के अंतिम एलिमेंट का अद्यतन एड्रेस वापस करना चाहिए। nextB और appendB प्रक्रियाएं अन्य इनपुट अनुक्रम के अनुरूप हैं।

मर्जिंग लिंक्ड लिस्ट

सामान्य मर्ज प्रक्रिया के उपयोग को स्पष्ट करने के लिए, यहां दो सरल लिंक की गई सूचियों को मर्ज करने के लिए कोड दिया गया है, जो पते R, S पर नोड्स से प्रारम्भ होती है। यहां हम मानते हैं कि प्रत्येक रिकॉर्ड x में पूर्णांक फ़ील्ड x.INFO और एड्रेस फ़ील्ड x.NEXT होता है जो अगले नोड की ओर पॉइंट करता है; जहां प्रत्येक सूची में info फ़ील्ड बढ़ते क्रम में हैं। मर्ज द्वारा इनपुट सूचियों को नष्ट कर दिया जाता है, और उनके नोड्स का उपयोग आउटपुट सूची बनाने के लिए किया जाता है।

procedure listmerge(R, S):

    procedure prec(r, s):
        return r.INFO < s.INFO

    procedure next(x):
        return x.NEXT

    procedure append(x, fin)
        if fin  Λ then fin.NEXT  x
        x.NEXT  Λ
        return x
     
    return merge(R, S, prec, next, append, next, append)

मर्जिंग वेक्टर्स

निम्नलिखित कोड अनुक्रमों के वास्तविक प्रतिनिधित्व से सामान्य मर्ज प्रक्रिया की स्वतंत्रता को दर्शाता है। यह दो साधारण सरणियों U[0] से U[m−1] और V[0] से V[n−1] तक फ्लोटिंग-पॉइंट संख्याओं के एलिमेंट को घटते क्रम में मिलाता है। इनपुट सरणियों को संशोधित नहीं किया गया है, और मानों का मर्ज किया गया अनुक्रम W[m+n−1] के माध्यम से तीसरे वेक्टर W[0] में संग्रहीत किया जाता है। जैसा कि C प्रोग्रामिंग लैंग्वेज में है, हम मानते हैं कि अभिव्यक्ति "&V" वेरिएबल V का एड्रेस देता है, "*p" वह वेरिएबल देता है जिसका एड्रेस p का मान है, और "&(X[i])" किसी भी ऐरे X और किसी पूर्णांक i के लिए "&(X[0]) + i" के बराबर है।

procedure arraymerge(U, m, V, n, W):

    procedure prec(r, s):
        return (*r) > (*s)

    procedure nextU(x):
        if x = &(U[m1]) then return Λ else return x + 1

    procedure nextV(x):
        if x = &(V[n1]) then return Λ else return x + 1

    procedure append(x, fin)
        if fin = Λ then fin  &(W[0])
        (*fin)  (*x)
        return fin + 1
        
    if m = 0 then U  Λ
    if n = 0 then V  Λ
    return merge(U, V, prec, nextU, append, nextV, append)

उदाहरण: निश्चित अभिन्न

इंटेग्रटिंग ओवर एन इंटरवल

निम्नलिखित प्रक्रिया वास्तविक रेखा के दिए गए इंटरवल [a,b] पर दिए गए वास्तविक-मूल्य वाले फ़ंक्शन f के अनुमानित अभिन्न f (x) dx की गणना करती है। उपयोग की जाने वाली संख्यात्मक विधि चरणों की दी गई संख्या n के साथ ट्रैपेज़ियम नियम है; वास्तविक संख्याएँ फ़्लोटिंग-पॉइंट संख्याओं द्वारा अनुमानित की जाती हैं

procedure Intg(f, a, b, n):
    float t, x, s; integer i
    if b = a then return 0
    x  a; s  f(a) / 2;
    for i from 1 to n1 do
        t  i/(n+1); x  (1t) * a + t * b;
        s  s + f(x)
    s  f(b) / 2
    return (b  a) * s / n

इंटेग्रटिंग ओवर ए  डिस्क

अब किसी दिए गए फ़ंक्शन को एकीकृत करने की समस्या पर विचार करें, दो तर्कों के साथ, डिस्क पर दिए गए केंद्र के साथ () और त्रिज्या दी गई है चर के परिवर्तन से इस समस्या को दो नेस्टेड एकल-चर इंटीग्रल में कम किया जा सकता है

निम्नलिखित कोड दाहिनी ओर का फॉर्मूला लागू करता है:

procedure DiskIntg(g, xc, yc, R, n)

    procedure gring(z):

        procedure gpolar(t):
            float x, y
            x  xc + z * cos(t)
            y  yc + z * sin(t)
            return g(x, y)

        integer m  round(n*z/R)
        return z * Intg(gpolar, 0, 2*π, m)

    return Intg(gring, 0, R, n)

यह कोड दो स्तरों में एकीकरण प्रक्रिया Intg का उपयोग करता है। बाहरी स्तर (अंतिम पंक्ति) 0 से तक भिन्न के लिए के अभिन्न अंग की गणना करने के लिए Intg का उपयोग करता है। आंतरिक स्तर (अंतिम से अगली पंक्ति) केंद्र और त्रिज्या वाले वृत्त पर की रेखा अभिन्न अंग के रूप में को परिभाषित करता है।

इतिहास

प्रक्रियात्मक पैरामीटर का आविष्कार इलेक्ट्रॉनिक कंप्यूटर के युग से पहले, गणितज्ञ अलोंजो चर्च द्वारा, गणना के उनके लैम्ब्डा-कैलकुलस मॉडल के हिस्से के रूप में किया गया था।

प्रोग्रामिंग लैंग्वेज सुविधा के रूप में प्रक्रियात्मक पैरामीटर ALGOL 60 द्वारा पेश किए गए थे। वास्तव में, ALGOL 60 में एक प्रभावी "कॉल बाय नेम" पैरामीटर-पासिंग तंत्र था जो प्रक्रियात्मक पैरामीटर के कुछ उपयोगों को सरल बना सकता था; जेन्सेन का उपकरण देखें।

प्रक्रियात्मक पैरामीटर एलआईएसपी प्रोग्रामिंग लैंग्वेज की अनिवार्य विशेषता थी, जिसने फ़ंक्शन क्लोजर या फनर्ग की अवधारणा भी पेश की। C प्रोग्रामिंग लैंग्वेज फ़ंक्शन पॉइंटर्स को पैरामीटर के रूप में पारित करने की अनुमति देती है, जो समान अंत को पूरा करती है, और प्रायः इवेंट-संचालित प्रोग्रामिंग में कॉलबैक और त्रुटि हैंडलर के रूप में उपयोग की जाती है। हालाँकि, केवल कुछ आधुनिक C कंपाइलर नेस्टेड फ़ंक्शन परिभाषाओं की अनुमति देते हैं, ताकि इसके अन्य उपयोग अपेक्षाकृत असामान्य हों। नेस्टेड प्रक्रिया परिभाषाओं के साथ, प्रक्रियात्मक पैरामीटर पास्कल में भी प्रदान किए गए थे; हालाँकि, चूँकि मानक पास्कल ने अलग संकलन की अनुमति नहीं दी थी, इसलिए उस लैंग्वेज में भी इस सुविधा का बहुत कम उपयोग किया गया था।

यह भी देखें