आइए रूबी में पैटर्न मैचिंग पर एक संक्षिप्त चर्चा से शुरू करते हैं, यह क्या करता है, और यह कोड पठनीयता को कैसे सुधारने में मदद कर सकता है।
यदि आप कुछ साल पहले की तरह मेरे जैसे हैं, तो आप इसे रेगेक्स में पैटर्न मैचिंग के साथ भ्रमित कर सकते हैं। यहां तक कि 'पैटर्न मैचिंग' की एक त्वरित गूगल खोज बिना किसी अन्य संदर्भ के आपको उस परिभाषा के बहुत करीब की सामग्री लाती है।
औपचारिक रूप से, पैटर्न मैचिंग किसी भी डेटा (चाहे वह कैरेक्टर्स का अनुक्रम हो, टोकन्स की एक श्रृंखला हो, एक टपल हो, या कुछ और) को अन्य डेटा के विरुद्ध जांचने की प्रक्रिया है।
प्रोग्रामिंग के संदर्भ में, भाषा की क्षमताओं के आधार पर, इसका अर्थ निम्नलिखित में से कोई भी हो सकता है:
- अपेक्षित डेटा टाइप के विरुद्ध मैच करना
- अपेक्षित हैश संरचना के विरुद्ध मैच करना (उदाहरण के लिए, विशिष्ट कुंजियों की उपस्थिति)
- अपेक्षित एरे लंबाई के विरुद्ध मैच करना
- मैच (या उनके एक भाग) को कुछ वेरिएबल्स में असाइन करना
पैटर्न मैचिंग में मेरा पहला प्रयास एलिक्सिर के माध्यम से था। एलिक्सिर में पैटर्न मैचिंग के लिए फर्स्ट-क्लास सपोर्ट है, इतना कि = ऑपरेटर वास्तव में match ऑपरेटर है, न कि साधारण असाइनमेंट।
इसका अर्थ है कि एलिक्सिर में, निम्न वास्तव में वैध कोड है:
iex> x = 1 iex> 1 = x
इसे ध्यान में रखते हुए, आइए रूबी 2.7+ के लिए नए पैटर्न मैचिंग सपोर्ट को देखें और हम इसका उपयोग आज से अपने कोड को अधिक पठनीय बनाने के लिए कैसे कर सकते हैं।
रूबी में case/in के साथ पैटर्न मैचिंग
रूबी एक विशेष case/in एक्सप्रेशन के साथ पैटर्न मैचिंग का समर्थन करता है। सिंटैक्स है:
case in # ... in # ... else # ... end
इसे case/when एक्सप्रेशन के साथ भ्रमित न करें। when और in ब्रांच को एक ही case में मिश्रित नहीं किया जा सकता।
यदि आप else एक्सप्रेशन प्रदान नहीं करते हैं, तो कोई भी विफल मैच NoMatchingPatternError उठाएगा।
रूबी में एरे में पैटर्न मैचिंग
पैटर्न मैचिंग का उपयोग डेटा टाइप्स, लंबाई या मानों के विरुद्ध पूर्व-आवश्यक संरचनाओं के लिए एरे का मिलान करने के लिए किया जा सकता है।
उदाहरण के लिए, निम्न सभी मैच हैं (ध्यान दें कि केवल पहला in मूल्यांकन किया जाएगा, क्योंकि case पहले मैच के बाद देखना बंद कर देता है):
case [1, 2, "Three"] in [Integer, Integer, String] "matches" in [1, 2, "Three"] "matches" in [Integer, *] "matches" # क्योंकि * एक स्प्रेड ऑपरेटर है जो कुछ भी मैच करता है in [a, *] "matches" # और वेरिएबल a का मान अब 1 है end
इस प्रकार का पैटर्न मैचिंग क्लॉज तब बहुत उपयोगी होता है जब आप एक मेथड कॉल से कई सिग्नल उत्पन्न करना चाहते हैं।
एलिक्सिर की दुनिया में, यह अक्सर उन ऑपरेशन्स को करते समय उपयोग किया जाता है जिनके :ok परिणाम और :error परिणाम दोनों हो सकते हैं, उदाहरण के लिए, डेटाबेस में इन्सर्ट करना।
यहां बताया गया है कि हम इसे बेहतर पठनीयता के लिए कैसे उपयोग कर सकते हैं:
def create case save(model_params) in [:ok, model] render :json => model in [:error, errors] render :json => errors end end # आपके कोड में कहीं, उदाहरण के लिए एक ग्लोबल हेल्पर या आपके मॉडल बेस क्लास में (एक अलग नाम के साथ)। def save(attrs) model = Model.new(attrs) model.save ? [:ok, model] : [:error, model.errors] end
रूबी में ऑब्जेक्ट्स में पैटर्न मैचिंग
आप रूबी में ऑब्जेक्ट्स का मिलान एक विशिष्ट संरचना को लागू करने के लिए भी कर सकते हैं:
case {a: 1, b: 2} in {a: Integer} "matches" # डिफ़ॉल्ट रूप से, सभी ऑब्जेक्ट मैच आंशिक होते हैं in {a: Integer, **} "matches" # और {a: Integer} के समान है in {a: a} "matches" # और वेरिएबल a का मान अब 1 है in {a: Integer => a} "matches" # और वेरिएबल a का मान अब 1 है in {a: 1, b: b} "matches" # और वेरिएबल b का मान अब 2 है in {a: Integer, **nil} "does not match" # यह केवल तभी मैच करेगा यदि ऑब्जेक्ट में a है और कोई अन्य कुंजी नहीं है end
यह किसी भी पैरामीटर्स के विरुद्ध मिलान के लिए मजबूत नियम लागू करते समय बहुत अच्छा काम करता है।
उदाहरण के लिए, यदि आप एक फैंसी ग्रीटर लिख रहे हैं, तो इसकी निम्न (दृढ़ता से रायated) संरचना हो सकती है:
def greet(hash = {}) case hash in {greeting: greeting, first_name: first_name, last_name: last_name} greet(greeting: greeting, name: "#{first_name} #{last_name}") in {greeting: greeting, name: name} puts "#{greeting}, #{name}" in {name: name} greet(greeting: "Hello", name: name) in {greeting: greeting} greet(greeting: greeting, name: "Anonymous") else greet(greeting: "Hello", name: "Anonymous") end end greet # Hello, Anonymous greet(name: "John") # Hello, John greet(first_name: "John", last_name: "Doe") # Hello, John Doe greet(greeting: "Bonjour", first_name: "John", last_name: "Doe") # Bonjour, John Doe greet(greeting: "Bonjour") # Bonjour, Anonymous
रूबी में वेरिएबल बाइंडिंग और पिनिंग
जैसा कि हमने उपरोक्त कुछ उदाहरणों में देखा है, पैटर्न मैचिंग पैटर्न के कुछ हिस्सों को मनमाने वेरिएबल्स में असाइन करने में वास्तव में उपयोगी है। इसे वेरिएबल बाइंडिंग कहा जाता है, और वेरिएबल से बाइंड करने के कई तरीके हैं:
- एक मजबूत टाइप मैच के साथ, उदाहरण के लिए
in [Integer => a]याin {a: Integer => a} - टाइप स्पेसिफिकेशन के बिना, उदाहरण के लिए
in [a, 1, 2]याin {a: a}। - वेरिएबल नाम के बिना, जो कुंजी नाम का उपयोग करने के लिए डिफ़ॉल्ट होता है, उदाहरण के लिए
in {a:}वेरिएबलaको कुंजीaपर मान के साथ परिभाषित करेगा। - बाकी को बाइंड करें, उदाहरण के लिए
in [Integer, *rest]याin {a: Integer, **rest}।
तब, जब हम किसी मौजूदा वेरिएबल को सब-पैटर्न के रूप में उपयोग करना चाहते हैं तो हम कैसे मैच कर सकते हैं? यह तब होता है जब हम ^ (पिन) ऑपरेटर के साथ वेरिएबल पिनिंग का उपयोग कर सकते हैं:
a = 1 case {a: 1, b: 2} in {a: ^a} "matches" end
आप इसका उपयोग तब भी कर सकते हैं जब एक वेरिएबल पैटर्न में ही परिभाषित हो, जिससे आप इस तरह शक्तिशाली पैटर्न लिख सकते हैं:
case order in {billing_address: {city:}, shipping_address: {city: ^city}} puts "both billing and shipping are to the same city" else raise "both billing and shipping must be to the same city" end
वेरिएबल बाइंडिंग के साथ उल्लेख करने के लिए एक महत्वपूर्ण विचित्रता यह है कि भले ही पैटर्न पूरी तरह से मैच न करे, वेरिएबल फिर भी बाइंड हो जाएगा। यह कभी-कभी उपयोगी हो सकता है।
लेकिन, ज्यादातर मामलों में, यह सूक्ष्म बग्स का कारण भी बन सकता है — इसलिए सुनिश्चित करें कि आप मैच के अंदर उपयोग किए गए शैडोड वेरिएबल मानों पर भरोसा नहीं करते हैं। उदाहरण के लिए, निम्न में, आप उम्मीद करेंगे कि सिटी "Amsterdam" होगी, लेकिन यह इसके बजाय "Berlin" होगी:
city = "Amsterdam" order = {billing_address: {city: "Berlin"}, shipping_address: {city: "Zurich"}} case order in {billing_address: {city:}, shipping_address: {city: ^city}} puts "both billing and shipping are to the same city" else puts "both billing and shipping must be to the same city" end puts city # Amsterdam के बजाय Berlin
रूबी के कस्टम क्लासेस का मिलान
आप कस्टम क्लासेस को रूबी में पैटर्न मैचिंग अवेयर बनाने के लिए कुछ विशेष मेथड्स इम्प्लीमेंट कर सकते हैं।
उदाहरण के लिए, एक यूजर को उसके first_name और last_name के विरुद्ध पैटर्न मैच करने के लिए, हम क्लास पर deconstruct_keys परिभाषित कर सकते हैं:
class User def deconstruct_keys(keys) {first_name: first_name, last_name: last_name} end end case user in {first_name: "John"} puts "Hey, John" end
deconstruct_keys का keys आर्गुमेंट उन कुंजियों को समाहित करता है जिनका पैटर्न में अनुरोध किया गया है। यदि उन सभी की गणना करना महंगा है तो यह रिसीवर के लिए केवल आवश्यक कुंजियों को प्रदान करने का एक तरीका है।
deconstruct_keys के समान ही, हम deconstruct का एक इम्प्लीमेंटेशन प्रदान कर सकते हैं ताकि ऑब्जेक्ट्स को एरे के रूप में पैटर्न मैच किया जा सके। उदाहरण के लिए, मान लें कि हमारे पास एक Location क्लास है जिसमें अक्षांश और देशांतर हैं। deconstruct_keys का उपयोग करके अक्षांश और देशांतर कुंजियों को प्रदान करने के अलावा, हम [latitude, longitude] के रूप में एक एरे भी उजागर कर सकते हैं:
class Location def deconstruct [latitude, longitude] end end case location in [Float => latitude, Float => longitude] puts "#{latitude}, #{longitude}" end
जटिल पैटर्न के लिए गार्ड्स का उपयोग करना
यदि हमारे पास जटिल पैटर्न हैं जिन्हें नियमित पैटर्न मैच ऑपरेटर्स के साथ दर्शाया नहीं जा सकता, तो हम मैच के लिए गार्ड प्रदान करने के लिए if (या unless) स्टेटमेंट का भी उपयोग कर सकते हैं:
case [1, 2] in [a, b] if b == a * 2 "matches" else "no match" end
case के बिना =>/in के साथ पैटर्न मैचिंग
यदि आप रूबी 3+ पर हैं, तो आपके पास और भी अधिक पैटर्न मैचिंग जादू तक पहुंच है। रूबी 3 से शुरू होकर, पैटर्न मैचिंग एक ही लाइन में बिना केस स्टेटमेंट के की जा सकती है:
[1, 2, "Three"] => [Integer => one, two, String => three] puts one # 1 puts two # 2 puts three # Three # Same as above [1, 2, "Three"] in [Integer => one, two, String => three]
चूंकि उपरोक्त सिंटैक्स में else क्लॉज नहीं है, यह तब सबसे उपयोगी होता है जब डेटा संरचना पहले से ज्ञात हो।
एक उदाहरण के रूप में, यह पैटर्न एक बेस कंट्रोलर के अंदर अच्छी तरह से फिट हो सकता है जो केवल एडमिन यूजर्स की अनुमति देता है:
class AdminController < AuthenticatedController before_action :verify_admin private def verify_admin Current.user => {role: :admin} rescue NoMatchingPatternError raise NotAllowedError end end
रूबी में पैटर्न मैचिंग: इस स्पेस को देखें
पहले, पैटर्न मैचिंग समझने में थोड़ा अजीब लग सकता है। कुछ के लिए, यह महिमामंडित ऑब्जेक्ट/एरे डिकंस्ट्रक्शन की तरह लग सकता है।
लेकिन अगर एलिक्सिर की लोकप्रियता कोई संकेत है, तो पैटर्न मैचिंग आपके शस्त्रागार में एक बेहतरीन टूल है। इसे एलिक्सिर पर फर्स्ट-हैंड अनुभव करने के बाद, मैं पुष्टि कर सकता हूं कि एक बार इसकी आदत हो जाने पर इसके बिना रहना मुश्किल है।
यदि आप रूबी 2.7 पर हैं, तो पैटर्न मैचिंग (case/in के साथ) अभी भी एक्सपेरिमेंटल है। रूबी 3 के साथ, case/in स्थिर हो गया है जबकि नए पेश किए गए सिंगल-लाइन पैटर्न मैचिंग एक्सप्रेशन एक्सपेरिमेंटल हैं। चेतावनियों को कोड में Warning[:experimental] = false या कमांड-लाइन कुंजी -W:no-experimental से बंद किया जा सकता है।
भले ही रूबी में पैटर्न मैचिंग अभी अपने शुरुआती चरण में है, मुझे उम्मीद है कि आपको यह परिचय उपयोगी लगा होगा और आप भविष्य के विकास के लिए उतने ही उत्साहित हैं जितना मैं हूँ!
P.S. यदि आप रूबी मैजिक पोस्ट को प्रेस से बाहर आते ही पढ़ना चाहते हैं, तो हमारे रूबी मैजिक न्यूज़लेटर की सदस्यता लें और कभी भी एक भी पोस्ट मिस न करें!