OCaml
पैटर्न मिलान
खोज…
फैक्टरियल फंक्शन पैटर्न मिलान का उपयोग करते हुए
let rec factorial n = match n with | 0 | 1 -> 1 | n -> n * (factorial (n - 1))
यह फ़ंक्शन दोनों मानों 0 और 1 पर मेल खाता है और उन्हें हमारी पुनरावर्ती परिभाषा के आधार मामले में मैप करता है। फिर अन्य सभी नंबर इस फ़ंक्शन के पुनरावर्ती कॉल के लिए मैप करते हैं।
बूलियन अभिव्यक्तियों का मूल्यांकन
हम बूलियन अभिव्यक्तियों के प्रकार को परिभाषित करते हैं जिनके परमाणुओं की पहचान तार के रूप में की जाती है
type expr =
| Atom of string
| Not of expr
| And of expr * expr
| Or of expr * expr
और इन भावों का मूल्यांकन एक oracle : string -> bool कर सकते हैं oracle : string -> bool परमाणुओं के मूल्यों को देते हुए जिन्हें हम निम्न प्रकार से पाते हैं:
let rec eval oracle = function
| Atom(name) -> oracle name
| Not(expr) -> not(eval oracle expr)
| And(expr1, expr2) -> (eval oracle expr1) && (eval oracle expr2)
| Or(expr1, expr2) -> (eval oracle expr1) || (eval oracle expr2)
देखें कि फ़ंक्शन कैसे स्पष्ट और पढ़ने में आसान है। पैटर्न मिलान के सही उपयोग के लिए धन्यवाद, इस फ़ंक्शन को पढ़ने वाले एक प्रोग्रामर को यह सुनिश्चित करने के लिए बहुत कम समय की आवश्यकता है कि यह सही तरीके से लागू हो।
नकारात्मक सामान्य रूप: गहरा पैटर्न मिलान
पैटर्न मिलान जटिल मूल्यों को फिर से बनाने की अनुमति देता है और यह किसी भी तरह से मूल्य के प्रतिनिधित्व के "बाहरी सबसे" स्तर तक सीमित नहीं है। इसे समझने के लिए, हम बूलियन एक्सप्रेशन को बूलियन एक्सप्रेशन में बदलकर फंक्शन को लागू करते हैं, जहां सभी नेगमेंट केवल परमाणुओं पर होते हैं, तथाकथित नेगेटिव नॉर्मल फॉर्म और इस रूप में एक्सप्रेशन को पहचानने वाला एक प्रेडिक्ट:
हम बूलियन अभिव्यक्तियों के प्रकार को परिभाषित करते हैं जिनके परमाणुओं की पहचान तार के रूप में की जाती है
type expr =
| Atom of string
| Not of expr
| And of expr * expr
| Or of expr * expr
आइए पहले हम एक सामान्य रूप में नकारात्मक रूप में पहचानने वाले भावों को परिभाषित करें:
let rec is_nnf = function
| (Atom(_) | Not(Atom(_))) -> true
| Not(_) -> false
| (And(expr1, expr2) | Or(expr1, expr2)) -> is_nnf expr1 && is_nnf expr2
जैसा कि आप देखते हैं, नॉटेड पैटर्न Not(Atom(_)) जैसे Not(Atom(_)) खिलाफ मैच करना संभव है। अब हम एक बूलियन अभिव्यक्ति की मैपिंग को एक सामान्य बूलियन अभिव्यक्ति को नकारने के लिए सामान्य रूप में लागू करते हैं:
let rec nnf = function
| (Atom(_) | Not(Atom(_))) as expr -> expr
| Not(And(expr1, expr2)) -> Or(nnf(Not(expr1)),nnf(Not(expr2)))
| Not(Or(expr1, expr2)) -> And(nnf(Not(expr1)),nnf(Not(expr2)))
| And(expr1, expr2) -> And(nnf expr1, nnf expr2)
| Or(expr1, expr2) -> Or(nnf expr1, nnf expr2)
| Not(Not(expr)) -> nnf expr
यह दूसरा फ़ंक्शन नेस्टेड पैटर्न के और भी अधिक उपयोग करता है। हम अंत में अपने कोड को एक निहितार्थ की उपेक्षा पर टॉपवेल में परीक्षण कर सकते हैं:
# let impl a b =
Or(Not(a), b);;
val impl : expr -> expr -> expr = <fun>
# let expr = Not(impl (Atom "A") (Atom "B"));;
val expr : expr = Not (Or (Not (Atom "A"), Atom "B"))
# nnf expr;;
- : expr = And (Atom "A", Not (Atom "B"))
# is_nnf (nnf expr);;
- : bool = true
OCaml प्रकार प्रणाली एक पैटर्न मिलान की थकावट को सत्यापित करने में सक्षम है। उदाहरण के लिए, यदि हम nnf फ़ंक्शन में Not(Or(expr1, expr2)) केस को nnf हैं, तो संकलक एक चेतावनी जारी करता है:
# let rec non_exhaustive_nnf = function
| (Atom(_) | Not(Atom(_))) as expr -> expr
| Not(And(expr1, expr2)) -> Or(nnf(Not(expr1)),nnf(Not(expr2)))
| And(expr1, expr2) -> And(nnf expr1, nnf expr2)
| Or(expr1, expr2) -> Or(nnf expr1, nnf expr2)
| Not(Not(expr)) -> nnf expr;;
Characters 14-254:
..............function
| (Atom(_) | Not(Atom(_))) as expr -> expr
| Not(And(expr1, expr2)) -> Or(nnf(Not(expr1)),nnf(Not(expr2)))
| And(expr1, expr2) -> And(nnf expr1, nnf expr2)
| Or(expr1, expr2) -> Or(nnf expr1, nnf expr2)
| Not(Not(expr)) -> nnf expr..
Warning 8: this pattern-matching is not exhaustive.
Here is an example of a case that is not matched:
Not (Or (_, _))
val non_exhaustive_nnf : expr -> expr = <fun>
(यह चेतावनी संकलक या दुभाषिया को लागू करते समय -w @8 विकल्प के साथ एक त्रुटि के रूप में मानी जा सकती है)
यह सुविधा संकलक द्वारा स्वीकार किए गए कार्यक्रमों में सुरक्षा और शुद्धता का एक बढ़ा हुआ स्तर प्रदान करती है। हालांकि इसके अन्य उपयोग हैं और उदाहरण के लिए खोजपरक प्रोग्रामिंग में इसका उपयोग किया जा सकता है। एक सामान्य रूप में रूपांतरण लिखना बहुत मजेदार है, फ़ंक्शन के कच्चे संस्करणों के साथ शुरू करना जो उपचार को परिष्कृत करने के लिए संकलक द्वारा प्रदान किए गए आसान मामलों को संभालने और गैर-मिलान वाले मामलों के उदाहरणों का उपयोग करते हैं।
रिकॉर्ड फ़ील्ड से मिलान करना
पैटर्न मिलान का उपयोग रिकॉर्डों के पुनर्निर्माण के लिए किया जा सकता है। हम एक पाठ फ़ाइल में रिकॉर्ड प्रकार का प्रतिनिधित्व करने वाले स्थानों के साथ इसका उदाहरण देते हैं, जैसे कि प्रोग्राम का स्रोत कोड।
type location = {
filename : string;
line: int;
column: int;
offset: int;
}
टाइप लोकेशन का मान x इस तरह से डिकंस्ट्रक्ट किया जा सकता है:
let { filename; line; column; offset; } = x
एक समान वाक्यविन्यास का उपयोग फ़ंक्शन को परिभाषित करने के लिए किया जा सकता है, उदाहरण के लिए स्थानों को प्रिंट करने के लिए एक फ़ंक्शन:
let print_location { filename; line; column; offset; } =
Printf.printf "%s: %d: %d" filename line column
या वैकल्पिक रूप से
let print_location = function { filename; line; column; offset; } ->
Printf.printf "%s: %d: %d" filename line column
रिकॉर्ड से मेल खाने वाले पैटर्न को रिकॉर्ड के सभी क्षेत्रों का उल्लेख करने की आवश्यकता नहीं है। चूंकि फ़ंक्शन offset फ़ील्ड का उपयोग नहीं करता है, इसलिए हम इसे छोड़ सकते हैं:
let print_location { filename; line; column; } =
Printf.printf "%s: %d: %d" filename line column
जब रिकॉर्ड एक मॉड्यूल में परिभाषित किया जाता है, तो यह पैटर्न में होने वाले पहले क्षेत्र को योग्य बनाने के लिए पर्याप्त है:
module Location =
struct
type t = {
filename : string;
line: int;
column: int;
offset: int;
}
end
let print_location { Location.filename; line; column; } =
Printf.printf "%s: %d: %d" filename line column
पैटर्न मिलान के साथ पुनरावर्ती सूची प्रसंस्करण
यहां हम प्रदर्शित करते हैं कि OCaml के पैटर्न मिलान वाक्यविन्यास का उपयोग करके सूचियों को कैसे पुन: संसाधित किया जाए।
let rec map f lst =
match lst with
| [] -> []
| hd::tl -> (f hd)::(map f tl)
इस मामले में, पैटर्न [] खाली सूची से मेल खाता है, जबकि hd::tl किसी भी सूची से मेल खाता है , जिसमें कम से कम एक तत्व है, और सूची के पहले तत्व को hd और शेष सूची (खाली हो सकता है) से जोड़ देगा। to tl ।
ध्यान दें कि hd::tl एक बहुत ही सामान्य पैटर्न है और किसी भी सूची से मेल खाएगा जो खाली नहीं है। हम एक विशिष्ट संख्या के तत्वों के साथ सूचियों पर मेल खाने वाले पैटर्न भी लिख सकते हैं:
(* Return the last element of a list. Fails if the list is empty. *)
let rec last lst =
match lst with
| [] -> failwith "Empty list"
| [x] -> x (* Equivalent to x::[], [x] matches a list with only one element *)
| hd::tl -> last tl
(* The second to last element of a list. *)
let rec second_to_last lst =
match lst with
| [] -> failwith "Empty list"
| x::[] -> failwith "Singleton list"
| fst::snd::[] -> snd
| hd::tl -> second_to_last tl
इसके अतिरिक्त, OCaml स्वयं सूचियों के तत्वों से मेल खाते पैटर्न का समर्थन करता है। हम सूची के अंदर तत्वों की संरचना के बारे में अधिक विशिष्ट हो सकते हैं, और OCaml सही फ़ंक्शन प्रकार का अनुमान लगाएगा:
(* Assuming a list of tuples, return a list with first element of each tuple. *)
let rec first_elements lst =
match lst with
| [] -> []
| (a, b)::tl -> a::(first_elements tl)
(* val first_elements : ('a * 'b) list -> 'a list = <fun> *)
इन पैटर्नों को एक साथ जोड़कर, हम किसी भी मनमाने ढंग से जटिल सूची को संसाधित कर सकते हैं।
पैटर्न मिलान का उपयोग करके फ़ंक्शन को परिभाषित करना
किसी function के अंतिम तर्क पर पैटर्न-मिलान शुरू करने के लिए कीवर्ड function का उपयोग किया जा सकता है। उदाहरण के लिए, हम एक फ़ंक्शन लिख सकते हैं जिसे sum कहा जाता sum , जो पूर्णांक की एक सूची के योग की गणना करता है
let rec sum = function
| [] -> 0
| h::t -> h + sum t
;;
val sum : int list -> int = <fun>