functional programming कार्यात्मक भाषाओं में 'पैटर्न मिलान' क्या है?




functional-programming pattern-matching (8)

संक्षिप्त उत्तर: पैटर्न मिलान उत्पन्न होता है क्योंकि कार्यात्मक भाषा बराबर चिह्न को असाइनमेंट के बजाय समानता के दावे के रूप में मानती हैं।

लंबा उत्तर: पैटर्न मिलान उस मान के "आकार" के आधार पर प्रेषण का एक रूप है जो इसे दिया गया है। एक कार्यात्मक भाषा में, आपके द्वारा परिभाषित डेटाटाइप आमतौर पर भेदभाव वाले संघों या बीजगणित डेटा प्रकारों के रूप में जाना जाता है। उदाहरण के लिए, एक (लिंक) सूची क्या है? एक लिंक्ड सूची किसी प्रकार की चीजों की List या तो रिक्त सूची Nil या किसी तत्व के प्रकार का a तत्व है जो एक List a (एक सूची की सूची) पर है। हास्केल में (कार्यात्मक भाषा मैं सबसे ज्यादा परिचित हूं), हम इसे लिखते हैं

data List a = Nil
            | Cons a (List a)

सभी भेदभाव वाले संघों को इस तरह से परिभाषित किया जाता है: एक प्रकार के पास इसे बनाने के विभिन्न तरीकों की निश्चित संख्या होती है; यहां Nil और Cons जैसे रचनाकारों को कन्स्ट्रक्टर कहा जाता है। इसका मतलब है कि प्रकार List a का एक मूल्य दो अलग-अलग रचनाकारों के साथ बनाया जा सकता था-इसमें दो अलग-अलग आकार हो सकते थे। तो मान लें कि हम सूची के पहले तत्व को प्राप्त करने के लिए एक head कार्य लिखना चाहते हैं। हास्केल में, हम इसे लिखेंगे

-- `head` is a function from a `List a` to an `a`.
head :: List a -> a
-- An empty list has no first item, so we raise an error.
head Nil        = error "empty list"
-- If we are given a `Cons`, we only want the first part; that's the list's head.
head (Cons h _) = h

चूंकि List a मान दो अलग-अलग प्रकार की हो सकती है, इसलिए हमें प्रत्येक को अलग से संभालने की आवश्यकता है; यह पैटर्न मिलान है। head x , यदि x पैटर्न Nil मेल खाता है, तो हम पहले मामले को चलाते हैं; यदि यह पैटर्न Cons h _ मेल खाता है, तो हम दूसरा रन करते हैं।

संक्षिप्त उत्तर, समझाया गया: मुझे लगता है कि इस व्यवहार के बारे में सोचने के सर्वोत्तम तरीकों में से एक यह है कि आप बराबर चिह्न के बारे में क्या सोचते हैं। घुंघराले-ब्रैकेट भाषाओं में, बड़े पैमाने पर, = असाइनमेंट को इंगित करता है: a = b अर्थ है " b में बनाना।" कई कार्यात्मक भाषाओं में, हालांकि, समानता के दावे को दर्शाता है: let Cons a (Cons b Nil) = frob x जोर देता है कि बाईं ओर की चीज़, Cons a (Cons b Nil) let Cons a (Cons b Nil) = frob x Cons a (Cons b Nil) , दाईं ओर की चीज़ के बराबर है, frob x ; इसके अलावा, बाईं ओर प्रयुक्त सभी चर दृश्यमान हो जाते हैं। यह फ़ंक्शन तर्कों के साथ भी हो रहा है: हम जोर देते हैं कि पहला तर्क Nil की तरह दिखता है, और यदि ऐसा नहीं होता है, तो हम जांच करते रहते हैं।

मैं कार्यात्मक प्रोग्रामिंग के बारे में पढ़ रहा हूं और मैंने देखा है कि कई लेखों में पैटर्न मिलान का उल्लेख कार्यात्मक भाषाओं की मुख्य विशेषताओं में से एक है।

क्या कोई जावा / सी ++ / जावास्क्रिप्ट डेवलपर के लिए समझा सकता है इसका क्या अर्थ है?


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

उपर्युक्त विकीबूक से यह एक अच्छी परिभाषा है:

तो पैटर्न मिलान चीजों को नाम निर्दिष्ट करने का तरीका है (या उन चीजों को उन नामों को बाध्य करना), और संभवतः एक ही समय में उप-अभिव्यक्तियों में अभिव्यक्तियों को तोड़ना (जैसा कि हमने मानचित्र की परिभाषा में सूची के साथ किया था)।


कई लोगों के लिए, कुछ आसान उदाहरण प्रदान किए जाने पर, एक नई अवधारणा को चुनना आसान है, इसलिए यहां हम जाते हैं:

मान लीजिए कि आपके पास तीन पूर्णांक की एक सूची है, और वह पहला और तीसरा तत्व जोड़ना चाहता था। पैटर्न मिलान के बिना, आप इसे इस तरह कर सकते हैं (हास्केल में उदाहरण):

Prelude> let is = [1,2,3]
Prelude> head is + is !! 2
4

अब, हालांकि यह एक खिलौना उदाहरण है, कल्पना करें कि हम चर के लिए पहले और तीसरे पूर्णांक को बांधना चाहते हैं और उन्हें जोड़ना चाहते हैं:

addFirstAndThird is =
    let first = head is
        third = is !! 3
    in first + third

डेटा संरचना से मूल्यों का यह निष्कर्षण यह है कि पैटर्न मिलान क्या करता है। आप मूल रूप से रुचि की जगहों के लिए बाध्य करने के लिए चर देकर कुछ की संरचना "दर्पण" करते हैं:

addFirstAndThird [first,_,third] = first + third

जब आप इस कार्य को [1,2,3] के साथ तर्क के रूप में कहते हैं, [1,2,3] [पहले, _ , तीसरे] के साथ एकीकृत किया जाएगा, पहले 1, तीसरा से 3 बाध्यकारी और 2 को छोड़कर ( _ है उन चीजों के लिए प्लेसहोल्डर जिनकी आपको परवाह नहीं है)।

अब, यदि आप केवल दूसरे तत्व के रूप में 2 के साथ सूचियों से मेल खाना चाहते हैं, तो आप इसे इस तरह से कर सकते हैं:

addFirstAndThird [first,2,third] = first + third

यह केवल 2 के साथ अपने दूसरे तत्व के रूप में सूचियों के लिए काम करेगा और अन्यथा एक अपवाद फेंक देगा, क्योंकि addFirstAndThird के लिए कोई परिभाषा गैर मिलान वाली सूचियों के लिए नहीं दी जाती है।

अब तक, हमने बाध्यकारी को नष्ट करने के लिए केवल मिलान पैटर्न का उपयोग किया था। इसके ऊपर, आप एक ही फ़ंक्शन की कई परिभाषाएं दे सकते हैं, जहां पहली मिलान करने वाली परिभाषा का उपयोग किया जाता है, इस प्रकार, पैटर्न मिलान "स्टीरियोइड पर स्विच स्टेटमेंट" जैसा छोटा होता है:

addFirstAndThird [first,2,third] = first + third
addFirstAndThird _ = 0

addFirstAndThird खुशी से पहले और तीसरे तत्व को 2 के साथ अपने दूसरे तत्व के रूप में जोड़ देगा, और अन्यथा "इसके माध्यम से गिरें" और "वापसी" 0. यह "स्विच-जैसी" कार्यक्षमता केवल फ़ंक्शन परिभाषाओं में उपयोग नहीं किया जा सकता है, उदाहरण के लिए:

Prelude> case [1,3,3] of [a,2,c] -> a+c; _ -> 0
0
Prelude> case [1,2,3] of [a,2,c] -> a+c; _ -> 0
4

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

Prelude> case (Just 1) of (Just x) -> succ x; Nothing -> 0
2
Prelude> case Nothing of (Just x) -> succ x; Nothing -> 0
0

बेशक, वे केवल खिलौने के उदाहरण थे, और मैंने औपचारिक या संपूर्ण स्पष्टीकरण देने की कोशिश भी नहीं की, लेकिन बुनियादी अवधारणा को समझने के लिए उन्हें पर्याप्त होना चाहिए।


पैटर्न मिलान वह जगह है जहां आपकी भाषा के लिए दुभाषिया आपके द्वारा दिए गए तर्कों की संरचना और सामग्री के आधार पर एक विशेष कार्य करेगा।

यह न केवल एक कार्यात्मक भाषा सुविधा है बल्कि कई अलग-अलग भाषाओं के लिए उपलब्ध है।

पहली बार जब मैं इस विचार में आया था तब मैंने प्रोलॉग सीखा था जहां यह वास्तव में भाषा के लिए केंद्र है।

जैसे

अंतिम ([LastItem], LastItem)।

अंतिम ([हेड | टेल], लास्टइटेम): - आखिरी (पूंछ, LastItem)।

उपरोक्त कोड एक सूची का अंतिम आइटम देगा। इनपुट तर्क पहला है और नतीजा दूसरा है।

यदि सूची में केवल एक ही आइटम है तो दुभाषिया पहला संस्करण चुन देगा और दूसरा तर्क पहले के बराबर सेट किया जाएगा यानी परिणाम को एक मान दिया जाएगा।

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


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

पैटर्न सिर्फ एक कदम आगे जाते हैं, और आगे के तर्कों को भी नष्ट कर सकते हैं। यह तर्क के मूल्य के आधार पर वास्तव में मिलान करने के लिए संभावित रूप से गार्ड का उपयोग भी कर सकता है। प्रदर्शित करने के लिए, मैं दिखाऊंगा कि जावास्क्रिप्ट पैटर्न पैटर्न से मेल खाता है।

function foo(a,b,c){} //no pattern matching, just a list of arguments

function foo2([a],{prop1:d,prop2:e}, 35){} //invented pattern matching in JavaScript

Foo2 में, यह एक सरणी होने की अपेक्षा करता है, यह दूसरे तर्क को अलग करता है, एक ऑब्जेक्ट को दो प्रोप (प्रोप 1, प्रोप 2) के साथ उम्मीद करता है और उन गुणों के मानों को चर और डी को आवंटित करता है, और फिर तीसरे तर्क की अपेक्षा करता है 35।

जावास्क्रिप्ट के विपरीत, पैटर्न मिलान वाले भाषाएं आमतौर पर एक ही नाम के साथ कई कार्यों को अनुमति देती हैं, लेकिन विभिन्न पैटर्न। इस तरह यह विधि अधिभार की तरह है। मैं erlang में एक उदाहरण देंगे:

fibo(0) -> 0 ;
fibo(1) -> 1 ;
fibo(N) when N > 0 -> fibo(N-1) + fibo(N-2) .

अपनी आंखों को थोड़ा धुंधला करें और आप इसे जावास्क्रिप्ट में कल्पना कर सकते हैं। ऐसा कुछ हो सकता है:

function fibo(0){return 0;}
function fibo(1){return 1;}
function fibo(N) when N > 0 {return fibo(N-1) + fibo(N-2);}

प्वाइंट यह है कि जब आप fibo कहते हैं, तो इसका उपयोग कार्यान्वयन तर्कों पर आधारित होता है, लेकिन जहां जावा प्रकारों को ओवरलोडिंग के एकमात्र साधन के रूप में सीमित करता है, पैटर्न मिलान अधिक कर सकता है।

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


यहां एक बहुत ही छोटा उदाहरण है जो पैटर्न मिलान उपयोगीता दिखाता है:

मान लीजिए कि आप एक सूची में एक तत्व को सॉर्ट करना चाहते हैं:

["Venice","Paris","New York","Amsterdam"] 

करने के लिए (मैंने "न्यूयॉर्क" को हल किया है)

["Venice","New York","Paris","Amsterdam"] 

एक और अनिवार्य भाषा में आप लिखेंगे:

function up(city, cities){  
    for(var i = 0; i < cities.length; i++){
        if(cities[i] === city && i > 0){
            var prev = cities[i-1];
            cities[i-1] = city;
            cities[i] = prev;
        }
    }
    return cities;
}

एक कार्यात्मक भाषा में आप इसके बजाय लिखेंगे:

let up list value =  
    match list with
        | [] -> []
        | previous::current::tail when current = value ->  current::previous::tail
        | current::tail -> current::(up tail value)

जैसा कि आप देख सकते हैं कि पैटर्न मिलान किए गए समाधान में कम शोर है, आप स्पष्ट रूप से देख सकते हैं कि अलग-अलग मामले क्या हैं और हमारी सूची को यात्रा और डिज़ाइन करना कितना आसान है।

मैंने यहां इसके बारे में एक और अधिक विस्तृत ब्लॉग पोस्ट लिखा here


पैटर्न मिलान को समझने के लिए तीन भागों की व्याख्या करने की आवश्यकता है:

  1. बीजगणित डेटा प्रकार।
  2. क्या पैटर्न मिलान है
  3. यह कमाल क्यों है।

संक्षेप में बीजगणित डेटा प्रकार

एमएल जैसी कार्यात्मक भाषाएं आपको "पृथक संघों" या "बीजगणित डेटा प्रकार" नामक सरल डेटा प्रकारों को परिभाषित करने की अनुमति देती हैं। ये डेटा संरचनाएं साधारण कंटेनर हैं, और इसे संक्षेप में परिभाषित किया जा सकता है। उदाहरण के लिए:

type 'a list =
    | Nil
    | Cons of 'a * 'a list

एक ढेर की तरह डेटा संरचना परिभाषित करता है। इसके बारे में इस सी # के बराबर सोचें:

public abstract class List<T>
{
    public class Nil : List<T> { }
    public class Cons : List<T>
    {
        public readonly T Item1;
        public readonly List<T> Item2;
        public Cons(T item1, List<T> item2)
        {
            this.Item1 = item1;
            this.Item2 = item2;
        }
    }
}

इसलिए, Cons और Nil पहचानकर्ता साधारण साधारण वर्ग को परिभाषित करते हैं, जहां of x * y * z * ... एक कन्स्ट्रक्टर और कुछ डेटा प्रकार परिभाषित करता है। कन्स्ट्रक्टर के पैरामीटर अज्ञात हैं, उन्हें स्थिति और डेटा प्रकार द्वारा पहचाना जाता है।

आप अपनी a list उदाहरण इस प्रकार बनाते हैं:

let x = Cons(1, Cons(2, Cons(3, Cons(4, Nil))))

जो समान है:

Stack<int> x = new Cons(1, new Cons(2, new Cons(3, new Cons(4, new Nil()))));

संक्षेप में पैटर्न मिलान

पैटर्न मिलान एक प्रकार का प्रकार परीक्षण है। तो आइए मान लें कि हमने ऊपर की तरह एक स्टैक ऑब्जेक्ट बनाया है, हम स्टैक को देखने और पॉप करने के तरीकों को निम्नानुसार कार्यान्वित कर सकते हैं:

let peek s =
    match s with
    | Cons(hd, tl) -> hd
    | Nil -> failwith "Empty stack"

let pop s =
    match s with
    | Cons(hd, tl) -> tl
    | Nil -> failwith "Empty stack"

उपर्युक्त विधियां निम्नलिखित सी # के बराबर हैं (हालांकि इस तरह लागू नहीं हैं)

public static T Peek<T>(Stack<T> s)
{
    if (s is Stack<T>.Cons)
    {
        T hd = ((Stack<T>.Cons)s).Item1;
        Stack<T> tl = ((Stack<T>.Cons)s).Item2;
        return hd;
    }
    else if (s is Stack<T>.Nil)
        throw new Exception("Empty stack");
    else
        throw new MatchFailureException();
}

public static Stack<T> Pop<T>(Stack<T> s)
{
    if (s is Stack<T>.Cons)
    {
        T hd = ((Stack<T>.Cons)s).Item1;
        Stack<T> tl = ((Stack<T>.Cons)s).Item2;
        return tl;
    }
    else if (s is Stack<T>.Nil)
        throw new Exception("Empty stack");
    else
        throw new MatchFailureException();
}

(लगभग हमेशा, एमएल भाषा रन-टाइम प्रकार-परीक्षण या कास्ट के बिना पैटर्न मिलान को कार्यान्वित करती है, इसलिए सी # कोड कुछ हद तक भ्रामक है। आइए कुछ हाथ-लहराते हुए कार्यान्वयन विवरण को ब्रश करें :))

संक्षेप में डेटा संरचना अपघटन

ठीक है, आइए चोटी विधि पर वापस जाएं:

let peek s =
    match s with
    | Cons(hd, tl) -> hd
    | Nil -> failwith "Empty stack"

यह चाल समझ रही है कि hd और tl पहचानकर्ता चर हैं (त्रुटि ... क्योंकि वे अपरिवर्तनीय हैं, वे वास्तव में "चर" नहीं हैं, लेकिन "मान";))। यदि s के प्रकार का Cons , तो हम इसके मूल्यों को कन्स्ट्रक्टर से बाहर खींचने जा रहे हैं और उन्हें hd और tl नामक चर से बांध सकते हैं।

पैटर्न मिलान उपयोगी है क्योंकि यह हमें इसकी सामग्री के बजाय डेटा आकार को इसके आकार से विघटित करने देता है। तो कल्पना करें कि क्या हम एक बाइनरी पेड़ को निम्नानुसार परिभाषित करते हैं:

type 'a tree =
    | Node of 'a tree * 'a * 'a tree
    | Nil

हम कुछ पेड़ घूर्णन को निम्नानुसार परिभाषित कर सकते हैं:

let rotateLeft = function
    | Node(a, p, Node(b, q, c)) -> Node(Node(a, p, b), q, c)
    | x -> x

let rotateRight = function
    | Node(Node(a, p, b), q, c) -> Node(a, p, Node(b, q, c))
    | x -> x

( let rotateRight = function कन्स्ट्रक्टर let rotateRight s = match s with ... लिए सिंटैक्स चीनी है let rotateRight s = match s with ... )

तो चर के लिए डेटा संरचना बाध्य करने के अलावा, हम भी इसमें ड्रिल कर सकते हैं। आइए मान लें कि हमारे पास नोड है let x = Node(Nil, 1, Nil) । अगर हम rotateLeft x कॉल rotateLeft x , तो हम पहले पैटर्न के विरुद्ध x परीक्षण करते हैं, जो मिलान करने में विफल रहता है क्योंकि सही बच्चे के पास Node बजाय Nil टाइप होता है। यह अगले पैटर्न पर जायेगा, x -> x , जो किसी भी इनपुट से मेल खाता है और इसे अनमोडिफाइड करता है।

तुलना के लिए, हम उपरोक्त विधियों को सी # में लिखेंगे:

public abstract class Tree<T>
{
    public abstract U Match<U>(Func<U> nilFunc, Func<Tree<T>, T, Tree<T>, U> nodeFunc);

    public class Nil : Tree<T>
    {
        public override U Match<U>(Func<U> nilFunc, Func<Tree<T>, T, Tree<T>, U> nodeFunc)
        {
            return nilFunc();
        }
    }

    public class Node : Tree<T>
    {
        readonly Tree<T> Left;
        readonly T Value;
        readonly Tree<T> Right;

        public Node(Tree<T> left, T value, Tree<T> right)
        {
            this.Left = left;
            this.Value = value;
            this.Right = right;
        }

        public override U Match<U>(Func<U> nilFunc, Func<Tree<T>, T, Tree<T>, U> nodeFunc)
        {
            return nodeFunc(Left, Value, Right);
        }
    }

    public static Tree<T> RotateLeft(Tree<T> t)
    {
        return t.Match(
            () => t,
            (l, x, r) => r.Match(
                () => t,
                (rl, rx, rr) => new Node(new Node(l, x, rl), rx, rr))));
    }

    public static Tree<T> RotateRight(Tree<T> t)
    {
        return t.Match(
            () => t,
            (l, x, r) => l.Match(
                () => t,
                (ll, lx, lr) => new Node(ll, lx, new Node(lr, x, r))));
    }
}

गंभीरता से

पैटर्न मिलान अद्भुत है

आप विज़िटर पैटर्न का उपयोग करके सी # में पैटर्न मिलान के समान कुछ लागू कर सकते हैं, लेकिन यह लगभग उतना ही लचीला नहीं है क्योंकि आप जटिल डेटा संरचनाओं को प्रभावी ढंग से विघटित नहीं कर सकते हैं। इसके अलावा, यदि आप पैटर्न मिलान का उपयोग कर रहे हैं, तो संकलक आपको बताएगा कि क्या आपने कोई मामला छोड़ा है । वह कितना भयानक है?

इस बारे में सोचें कि आप पैटर्न # मिलान के बिना सी # या भाषाओं में समान कार्यक्षमता कैसे कार्यान्वित करेंगे। इस बारे में सोचें कि आप टेस्ट-टेस्ट के बिना इसे कैसे करेंगे और रनटाइम पर बने रहेंगे। यह निश्चित रूप से कठिन नहीं है , बस बोझिल और भारी है। और आपके पास यह सुनिश्चित करने के लिए कंपाइलर जांच नहीं है कि आपने हर मामले को कवर किया है।

तो पैटर्न मिलान आपको डेटा संरचनाओं को एक बहुत ही सुविधाजनक, कॉम्पैक्ट सिंटैक्स में विघटित करने और नेविगेट करने में मदद करता है, यह संकलक को कम से कम थोड़ा सा कोड आपके तर्क की जांच करने में सक्षम बनाता है। यह वास्तव में एक हत्यारा सुविधा है।


पैटर्न मिलान आपको कोड की शाखा चुनने के लिए कुछ पैटर्न के विरुद्ध एक मान (या ऑब्जेक्ट) से मिलान करने की अनुमति देता है। सी ++ बिंदु दृश्य से, यह switch स्टेटमेंट के समान ही लग सकता है। कार्यात्मक भाषाओं में, पैटर्न मिलान का उपयोग मानक आदिम मानों जैसे कि पूर्णांक पर मिलान के लिए किया जा सकता है। हालांकि, यह रचना प्रकारों के लिए अधिक उपयोगी है।

सबसे पहले, आइए आदिम मूल्यों पर पैटर्न मिलान का प्रदर्शन करें (विस्तारित छद्म-सी ++ switch का उपयोग करके):

switch(num) {
  case 1: 
    // runs this when num == 1
  case n when n > 10: 
    // runs this when num > 10
  case _: 
    // runs this for all other cases (underscore means 'match all')
}

स्कैंड का उपयोग कार्यात्मक डेटा प्रकारों जैसे टुपल्स (जो आपको एक ही मान में एकाधिक ऑब्जेक्ट्स स्टोर करने की अनुमति देता है) और भेदभाव वाले यूनियनों से संबंधित है जो आपको प्रकार बनाने की अनुमति देते हैं जिसमें कई विकल्पों में से एक हो सकता है। यह enum तरह थोड़ा लगता है सिवाय इसके कि प्रत्येक लेबल कुछ मूल्य भी ले जा सकते हैं। एक छद्म-सी ++ वाक्यविन्यास में:

enum Shape { 
  Rectangle of { int left, int top, int width, int height }
  Circle of { int x, int y, int radius }
}

प्रकार के Shape मूल्य में अब सभी निर्देशांक या केंद्र और त्रिज्या के साथ Circle साथ Rectangle हो सकता है। पैटर्न मिलान आपको Shape प्रकार के साथ काम करने के लिए फ़ंक्शन लिखने की अनुमति देता है:

switch(shape) { 
  case Rectangle(l, t, w, h): 
    // declares variables l, t, w, h and assigns properties
    // of the rectangle value to the new variables
  case Circle(x, y, r):
    // this branch is run for circles (properties are assigned to variables)
}

अंत में, आप नेस्टेड पैटर्न का भी उपयोग कर सकते हैं जो दोनों सुविधाओं को गठबंधन करते हैं। उदाहरण के लिए आप Circle(0, 0, radius) का उपयोग उन सभी आकृतियों के लिए मिलान कर सकते हैं, जो बिंदु [0, 0] में केंद्र रखते हैं और कोई त्रिज्या है (त्रिज्या का मान नए चर radius को सौंपा जाएगा)।

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





terminology