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


127

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

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


जवाबों:


141

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

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

संक्षेप में बीजीय डेटा प्रकार

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

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

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

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"

ऊपर दिए गए तरीके समतुल्य हैं (हालाँकि ऐसा लागू नहीं किया गया है) निम्नलिखित C #:

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();
}

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

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

ठीक है, चलिए वापस चलते हैं:

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

तुलना के लिए, हम ऊपर दिए गए तरीकों को C # में लिखेंगे:

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))));
    }
}

गंभीरता के लिए।

पैटर्न का मेल कमाल का है

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

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

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


+1 लेकिन अन्य भाषाओं के बारे में मत भूलिए, जैसे कि मैथेमेटिका जैसे पैटर्न से मेल खाते हों।
जद

1
चर ", लेकिन" मूल्यों "errm ... क्योंकि वे अडिग रहे हैं, वे वास्तव में नहीं कर रहे हैं" ";)" वे कर रहे हैं चर; यह परिवर्तनशील किस्म है जो गुमराह है । फिर भी, उत्कृष्ट जवाब!
डोभाल

3
"लगभग हमेशा, एमएल भाषाएँ रन-टाइम टाइप-टेस्ट या कास्ट के बिना पैटर्न मिलान को लागू करती हैं" <- यह कैसे काम करता है? क्या आप मुझे एक प्राइमर की ओर इशारा कर सकते हैं?
डेविड मोल्स

1
@DavidMoles: प्रकार प्रणाली यह सुनिश्चित करती है कि सभी रन-टाइम चेकों को माडल माचिस के माध्यम से समाप्त करना संभव है और यह निरर्थक नहीं है। यदि आप SML, OCaml या F # एक पैटर्न मैच की भाषा खिलाने की कोशिश करते हैं, जो संपूर्ण नहीं है या इसमें अतिरेक नहीं है, तो संकलक आपको संकलन समय पर चेतावनी देगा। यह एक अत्यंत शक्तिशाली विशेषता है क्योंकि यह आपको अपने कोड को फिर से व्यवस्थित करके रन-टाइम चेक को समाप्त करने की अनुमति देता है, अर्थात आपके पास अपने सही साबित होने के पहलू हो सकते हैं। इसके अलावा, यह समझना आसान है!
JD

@JonHarrop मैं देख सकता हूं कि यह कैसे काम करेगा (प्रभावी रूप से यह गतिशील संदेश प्रेषण के समान है) लेकिन मैं यह नहीं देख सकता कि रन-टाइम में आप किस प्रकार के परीक्षण के बिना एक शाखा का चयन करते हैं।
डेविड मोल्स

33

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

दीर्घ उत्तर: पैटर्न का मिलान उस दिए गए मूल्य के "आकार" के आधार पर प्रेषण का एक रूप है। एक कार्यात्मक भाषा में, आपके द्वारा परिभाषित डेटाटाइप्स आमतौर पर वे होते हैं जिन्हें विवेकहीन संघ या बीजगणितीय डेटा प्रकार के रूप में जाना जाता है। उदाहरण के लिए, क्या (लिंक की गई) सूची है? Listकुछ प्रकार की चीजों की एक लिंक की गई सूची aया तो खाली सूची Nilया कुछ प्रकार के a Consएड का तत्व है List a( 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जिसका अर्थ है "मेक aइन b"। बहुत सी कार्यात्मक भाषाओं में, हालांकि, =समानता के दावे को निरूपित let Cons a (Cons b Nil) = frob x करता है : यह दावा करता है कि बाईं तरफ Cons a (Cons b Nil)की चीज़, दाईं ओर की चीज़ के बराबर है frob x; इसके अलावा, बाईं ओर उपयोग किए गए सभी चर दिखाई देते हैं। फ़ंक्शन तर्कों के साथ भी यही हो रहा है: हम दावा करते हैं कि पहला तर्क जैसा दिखता है Nil, और यदि ऐसा नहीं होता है, तो हम जाँच करते रहते हैं।


बराबरी के संकेत के बारे में सोचने का एक दिलचस्प तरीका क्या है। साझा करने के लिए धन्यवाद!
जहाराली

2
क्या Consमतलब है?
रॉयमुनसन

2
@Roymunson: Consहै विपक्ष tructor है कि एक सिर से बाहर एक (जुड़े) सूची बनाता है ( a) और एक पूंछ ( List a)। नाम लिस्प से आता है। हास्केल में, अंतर्निहित सूची प्रकार के लिए, यह :ऑपरेटर है (जो अभी भी "विपक्ष" उच्चारण है)।
एंटाल स्पेक्टर-ज़बूसकी

23

इसका मतलब है कि लिखने के बजाय

double f(int x, int y) {
  if (y == 0) {
    if (x == 0)
      return NaN;
    else if (x > 0)
      return Infinity;
    else
      return -Infinity;
  } else
     return (double)x / y;
}

तुम लिख सकते हो

f(0, 0) = NaN;
f(x, 0) | x > 0 = Infinity;
        | else  = -Infinity;
f(x, y) = (double)x / y;

अरे, C ++ पैटर्न मिलान का भी समर्थन करता है।

static const int PositiveInfinity = -1;
static const int NegativeInfinity = -2;
static const int NaN = -3;

template <int x, int y> struct Divide {
  enum { value = x / y };
};
template <bool x_gt_0> struct aux { enum { value = PositiveInfinity }; };
template <> struct aux<false> { enum { value = NegativeInfinity }; };
template <int x> struct Divide<x, 0> {
  enum { value = aux<(x>0)>::value };
};
template <> struct Divide<0, 0> {
  enum { value = NaN };
};

#include <cstdio>

int main () {
    printf("%d %d %d %d\n", Divide<7,2>::value, Divide<1,0>::value, Divide<0,0>::value, Divide<-1,0>::value);
    return 0;
};

1
स्काला में: आयात डबल._ डिफ डिवाइड = {मान: (डबल, डबल) => मान मैच {केस (0,0) => NaN केस (x, 0) => अगर (x> 0) पॉजिटिवइंफिनिटी और नेगेटिव इनफिनिटी केस (x, y) => x / y}}
fracca

12

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

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

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);}

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

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


8

पैटर्न मिलान आपको कोड की एक शाखा का चयन करने के लिए कुछ पैटर्न के खिलाफ एक मूल्य (या एक वस्तु) से मेल खाने की अनुमति देता है। C ++ के दृष्टिकोण से, यह 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अब Rectangleसभी निर्देशांक के Circleसाथ या केंद्र और त्रिज्या के साथ हो सकता है। पैटर्न मिलान आपको 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)।

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


5

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

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

मुझे पहली बार इस विचार के बारे में पता चला कि जब मैंने यह सीखा कि यह वास्तव में भाषा का केंद्र है।

जैसे

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

अंतिम ([हेड | टेल], लास्ट इटेम): - लास्ट (टेल, लास्ट इटेम)।

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

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

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


जैसा कि आप उदाहरण से देख सकते हैं कि दुभाषिया एक एकल तर्क को कई चर में स्वतः ही तोड़ सकता है (उदाहरण [हेड | टेल])
charlieb

4

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

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

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

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


3

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

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

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


3
अगली बार मैं इस सवाल का ज़िक्र करूँगा कि मैंने विकिपीडिया पहले ही पढ़ लिया है और यह बहुत बुरा स्पष्टीकरण देता है।
रोमन

2

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

मान लें कि आप किसी सूची में एक तत्व को क्रमबद्ध करना चाहते हैं:

["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)

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

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

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.