
यह लेख कार्यात्मक प्रोग्रामिंग, सादगी और प्रोग्रामिंग इंटरफेस के डिजाइन के बारे में अधिक जानने के प्रयास में रूबी के पतित रूप के माध्यम से एक लक्ष्यहीन यात्रा पर केंद्रित है।
मान लीजिए कि कोड का प्रतिनिधित्व करने का एकमात्र तरीका एक लंबोदर अभिव्यक्ति है, और उपलब्ध एकमात्र डेटा संरचना एक सरणी है:
square = ->(x) { x * x } square.(4)
ये कार्यात्मक प्रोग्रामिंग की मूल बातें हैं: हमारे पास कार्य केवल एक चीज है। आइए एक ही शैली में वास्तविक कोड के समान कुछ और लिखने की कोशिश करें। आइए देखें कि हम कितनी पीड़ा के बिना जा सकते हैं।
मान लीजिए हम लोगों के बारे में जानकारी रखने वाले डेटाबेस के साथ काम करना चाहते हैं, और किसी ने हमें आंतरिक भंडारण के साथ बातचीत करने के लिए कई कार्य प्रदान किए हैं। हम एक उपयोगकर्ता इंटरफ़ेस और इनपुट सत्यापन जोड़ना चाहते हैं।
इस प्रकार हम भंडार से संपर्क करेंगे:
insert_person.(name,birthdate,gender)
सबसे पहले, हमें किसी व्यक्ति को डेटाबेस में जोड़ने में सक्षम होना चाहिए। इस स्थिति में, इनपुट डेटा को सत्यापित किया जाना चाहिए। हम इस डेटा को मानक इनपुट स्ट्रीम से निकालेंगे (हम मान लेते हैं कि
gets
बिल्ट-इन फ़ंक्शंस हैं और उम्मीद के मुताबिक काम करते हैं):
puts "Name?" name = gets puts "Birthdate?" birthdate = gets puts "Gender?" gender = gets
हमें डेटा को मान्य करने और डेटाबेस में जोड़ने के लिए एक फ़ंक्शन की आवश्यकता है। वह कैसे दिख सकती है? इसे व्यक्ति की विशेषताओं को स्वीकार करना चाहिए और सत्यापन और प्रविष्टि सफल होने पर, या कुछ गलत होने पर त्रुटि संदेश, या तो
id
वापस करना चाहिए। चूंकि हमारे पास कोई अपवाद या हैश टेबल (केवल सरणियां) नहीं हैं, इसलिए हमें रचनात्मक रूप से सोचना होगा।
आइए सहमत हैं कि हमारे आवेदन में सभी व्यावसायिक तर्क विधियां दो तत्वों की एक सरणी लौटाती हैं: पहला तत्व फ़ंक्शन का मूल्य है जब यह सफलतापूर्वक पूरा होता है, और दूसरा तत्व एक त्रुटि संदेश के साथ एक स्ट्रिंग है। सरणी की कोशिकाओं में से एक में मूल्य (
nil
) की मौजूदगी या अनुपस्थिति ऑपरेशन की सफलता या विफलता को इंगित करती है।
अब जब हम जानते हैं कि क्या स्वीकार करने की आवश्यकता है और क्या वापस करने की आवश्यकता है, तो हम स्वयं कार्य लिखना शुरू करेंगे:
add_person = ->(name,birthdate,gender) { return [nil,"Name is required"] if String(name) == '' return [nil,"Birthdate is required"] if String(birthdate) == '' return [nil,"Gender is required"] if String(gender) == '' return [nil,"Gender must be 'male' or 'female'"] if gender != 'male' && gender != 'female' id = insert_person.(name,birthdate,gender) [[name,birthdate,gender,id],nil] }
यदि आप नहीं जानते कि
String()
क्या है
String()
, तो यह फ़ंक्शन खाली स्ट्रिंग लौटाता है यदि
nil
पास किया जाता है।
हम इस फ़ंक्शन का उपयोग लूप में करना चाहते हैं जब तक कि उपयोगकर्ता सही डेटा प्रदान न करे, कुछ इस तरह से:
invalid = true while invalid puts "Name?" name = gets puts "Birthdate?" birthdate = gets puts "Gender?" gender = gets result = add_person.(name,birthdate,gender) if result[1] == nil puts "Successfully added person #{result[0][0]}" invalid = false else puts "Problem: #{result[1]}" end end
बेशक, हमने यह नहीं कहा कि आप साइकिल का उपयोग नहीं कर सकते हैं :) लेकिन मान लीजिए कि हमारे पास यह नहीं है।
लूप्स केवल कार्य हैं (जिन्हें पुनरावर्ती कहा जाता है)
पाश करने के लिए, हम बस एक फ़ंक्शन में अपना कोड लपेटते हैं और जब तक हमें वांछित परिणाम नहीं मिलता है, तब तक इसे पुन: कॉल करें।
get_new_person = -> { puts "Name?" name = gets puts "Birthdate?" birthdate = gets puts "Gender?" gender = gets result = add_person.(name,birthdate,gender) if result[1] == nil puts "Successfully added person #{result[0][0]}" result[0] else puts "Problem: #{result[1]}" get_new_person.() end } person = get_new_person.()
हम यह मान सकते हैं कि हमारे कोड में बहुत सारे चेक होंगे जैसे
if result[1] == nil
, तो चलो उन्हें एक फ़ंक्शन में लपेटें। कार्यों के बारे में महान बात यह है कि वे आपको संरचना का पुन: उपयोग करने की अनुमति देते हैं, तर्क नहीं। यहां संरचना एक त्रुटि के लिए जाँच कर रही है और सफलता या विफलता पर दो कार्यों में से एक को बुला रही है।
handle_result = ->(result,on_success,on_error) { if result[1] == nil on_success.(result[0]) else on_error.(result[1]) end }
get_new_person
फ़ंक्शन
get_new_person
एक अधिक अमूर्त त्रुटि हैंडलिंग विधि का उपयोग करता है:
get_new_person = -> { puts "Name?" name = gets.chomp puts "Birthdate?" birthdate = gets.chomp puts "Gender?" gender = gets.chomp result = add_person.(name,birthdate,gender) handle_result.(result, ->((id,name,birthdate,gender)) { puts "Successfully added person #{id}" [id,name,birthdate,gender,id] }, ->(error_message) { puts "Problem: #{error_message}" get_new_person.() } ) } person = get_new_person.()
ध्यान दें कि
handle_result
का उपयोग करके आप सरणी अनुक्रमण का उपयोग करने के बजाय स्पष्ट रूप से चर नाम दे सकते हैं। अब हम न केवल दोस्ताना नाम
error_message
उपयोग कर सकते हैं, बल्कि सरणी को भागों में "तोड़" सकते हैं और इसे अलग-अलग फ़ंक्शन मापदंडों के रूप में उपयोग कर सकते हैं
((id,name,birthdate,gender))
प्रपत्र
((id,name,birthdate,gender))
।
अभी तक तो अच्छा है। यह कोड थोड़ा अजीब लग सकता है, लेकिन यह क्रिया और भ्रमित नहीं है।
अधिक विशेषताएं - क्लीनर कोड
यह असामान्य लग सकता है कि कहीं भी हमारे कोड में हमारे "व्यक्ति" के लिए डेटा संरचना की औपचारिक परिभाषा नहीं थी। हमारे पास बस एक सरणी है, और हम सहमत हुए कि पहला तत्व नाम है, दूसरा जन्म तिथि है, आदि विचार काफी सरल है, लेकिन आइए कल्पना करें कि हमें एक नया क्षेत्र जोड़ने की आवश्यकता है: शीर्षक। अगर हम ऐसा करने की कोशिश करते हैं तो हमारे कोड का क्या होगा?
अब डेटाबेस
insert_person
और
insert_person
नए संस्करण प्रदान करता है:
insert_person.(name,birthdate,gender,title) update_person.(name,birthdate,gender,title,id)
add_person
विधि बदलें:
add_person = ->(name,birthdate,gender,title) { return [nil,"Name is required"] if String(name) == '' return [nil,"Birthdate is required"] if String(birthdate) == '' return [nil,"Gender is required"] if String(gender) == '' return [nil,"Gender must be 'male' or 'female'"] if gender != 'male' && gender != 'female' id = insert_person.(name,birthdate,gender,title) [[name,birthdate,gender,title,id],nil] }
चूंकि हम एक नए क्षेत्र का उपयोग कर रहे हैं, इसलिए हमें
get_new_person
अपडेट करना
get_new_person
। अहम:
get_new_person = -> { puts "Name?" name = gets.chomp puts "Birthdate?" birthdate = gets.chomp puts "Gender?" gender = gets.chomp puts "Title?" title = gets.chomp result = add_person.(name,birthdate,gender,title) handle_result.(result, ->((name,birthdate,gender,title,id)) { puts "Successfully added person #{id}" [id,name,birthdate,gender,title,id] }, ->(error_message) { puts "Problem: #{error_message}" get_new_person.() } ) }
यह एप्लिकेशन घटकों की मजबूत कनेक्टिविटी का सार दिखाता है।
get_new_person
को विशिष्ट रिकॉर्ड फ़ील्ड के बारे में बिल्कुल भी चिंता करने की आवश्यकता नहीं है। फ़ंक्शन को बस उन्हें पढ़ना चाहिए और फिर उन्हें
add_person
पास करना
add_person
। आइए देखें कि यदि हम कई नए कार्यों में कोड निकालते हैं तो हम इसे कैसे ठीक कर सकते हैं:
read_person_from_user = -> { puts "Name?" name = gets.chomp puts "Birthdate?" birthdate = gets.chomp puts "Gender?" gender = gets.chomp puts "Title?" title = gets.chomp [name,birthdate,gender,title] } person_id = ->(*_,id) { id } get_new_person = -> { handle_result.(add_person.(*read_person_from_user.()) ->(person) { puts "Successfully added person #{person_id.(person)}" person }, ->(error_message) { puts "Problem: #{error_message}" get_new_person.() } ) }
अब हम किसी व्यक्ति के बारे में डेटा कैसे संग्रहित करते हैं, इसके बारे में जानकारी दो कार्यों में छिपी है:
read_person_from_user
और
person_id
। अब हमें
get_new_person
को बदलने की आवश्यकता नहीं है यदि हम रिकॉर्ड में अधिक फ़ील्ड जोड़ना चाहते हैं।
यदि
*
कोड आपको परेशान करता है, तो यहां एक संक्षिप्त विवरण दिया गया है:
*
आपको सरणी को तर्कों की सूची के रूप में पारित करने की अनुमति देता है और इसके विपरीत।
person_id
हम पैरामीटर सूची
*_, id
उपयोग करते हैं, जो रूबी को पिछले एक को छोड़कर सभी तर्कों को
_
सरणी में रखने के लिए कहता है (हमें इस सरणी में कोई दिलचस्पी नहीं है, यही कारण है कि यह नाम है), और अंतिम को
id
चर में डाल दिया। यह केवल रूबी 1.9 में काम करता है; 1.8 में, केवल अंतिम तर्क
*
सिंटैक्स का उपयोग कर सकता है। फिर, जब हम
add_person
कॉल करते हैं, तो हम
add_person
के परिणाम के साथ
*
उपयोग करते हैं। चूँकि
read_person_from_user
एक सरणी देता है, इसलिए हम इस सरणी का उपयोग तर्क सूची के रूप में करना चाहते हैं क्योंकि
add_person
स्पष्ट तर्क स्वीकार करता है। यह वही है जो
*
करता है। बहुत बढ़िया!
यदि आप कोड पर लौटते हैं, तो आप देख सकते हैं कि
read_person_from_user
और
person_id
अभी भी काफी मजबूती से जुड़े हुए हैं। वे दोनों जानते हैं कि हम डेटा कैसे स्टोर करते हैं। इसके अलावा, अगर हमने अपने डेटाबेस से डेटा को संसाधित करने के लिए नई सुविधाएँ जोड़ी हैं, तो हमें ऐसे फ़ंक्शन का उपयोग करना होगा जो कि सरणी की आंतरिक संरचना के बारे में भी जानते हैं।
हमें किसी प्रकार की डेटा संरचना की आवश्यकता है।
डेटा संरचनाएं केवल कार्य हैं
साधारण रूबी में, हमने पहले ही इस समय तक एक वर्ग या कम से कम
Hash
आयोजन किया होगा, लेकिन हम उनका उपयोग नहीं कर सकते। क्या हम केवल कार्यों के साथ एक वास्तविक डेटा संरचना बना सकते हैं? यह पता चला है, हम कर सकते हैं, अगर हम एक ऐसा फ़ंक्शन बनाते हैं जो डेटा संरचना की विशेषता के रूप में इसके पहले तर्क पर विचार करता है:
new_person = ->(name,birthdate,gender,title,id=nil) { return ->(attribute) { return id if attribute == :id return name if attribute == :name return birthdate if attribute == :birthdate return gender if attribute == :gender return title if attribute == :title nil } } dave = new_person.("Dave","06-01-1974","male","Baron") puts dave.(:name)
new_person
एक
new_person
के रूप में कार्य करता है, लेकिन किसी ऑब्जेक्ट को वापस करने के बजाय (जो हमारे पास नहीं है), यह एक फ़ंक्शन देता है, जिसे जब कहा जाता है, तो हमें एक विशेष रिकॉर्ड के विभिन्न विशेषताओं के मूल्यों को वापस कर सकता है।
कक्षा के साथ समान व्यवहार लागू करने की तुलना करें:
class Person attr_reader :id, :name, :birthdate, :gender, :title def initialize(name,birthdate,gender,title,id=nil) @id = id @name = name @birthdate = birthdate @gender = gender @title = title end end dave = Person.new("Dave","06-01-1974","male","Baron") puts dave.name puts dave.gender
यह तो इंटरेस्टिंग है। कोड के इन टुकड़ों का आकार लगभग समान है, लेकिन वर्ग के साथ संस्करण
विशेष निर्माण का उपयोग करता है। विशेष निर्माण अनिवार्य रूप से जादू है जो एक प्रोग्रामिंग भाषा प्रदान करता है। इस कोड को समझने के लिए, आपको यह जानना होगा:
- क्या
class
मतलब है - क्लास के नाम के साथ
new
कॉल करना initialize
मेथड कहता है - एक विधि क्या है
- वैरिएबल नाम से पहले
@
यह कक्षा का एक निजी उदाहरण चर बनाता है - एक वर्ग और उसके उदाहरण के बीच का अंतर
attr_reader
क्या करता attr_reader
कार्यात्मक संस्करण को समझने के लिए, आपको केवल यह जानना चाहिए:
- कैसे एक समारोह को परिभाषित करने के लिए
- किसी फ़ंक्शन को कैसे कॉल करें
जैसा कि मैंने कहा, यह मुझे दिलचस्प लगता है। हमारे पास अनिवार्य रूप से एक ही काम करने के दो तरीके हैं, लेकिन उनमें से एक को दूसरे की तुलना में आपसे अधिक विशिष्ट ज्ञान की आवश्यकता होती है।
ठीक है, अब हमारे पास एक वास्तविक डेटा संरचना है। आइए इसके साथ काम करने के लिए अपना कोड बदलें, न कि सरणियों के साथ:
read_person_from_user = -> { puts "Name?" name = gets.chomp puts "Birthdate?" birthdate = gets.chomp puts "Gender?" gender = gets.chomp puts "Title?" title = gets.chomp new_person.(name,birthdate,gender,title) } add_person = ->(person) { return [nil,"Name is required"] if String(person.(:name)) == '' return [nil,"Birthdate is required"] if String(person.(:birthdate)) == '' return [nil,"Gender is required"] if String(person.(:gender)) == '' return [nil,"Gender must be 'male' or 'female'"] if person.(:gender) != 'male' && person.(:gender) != 'female' id = insert_person.(person.(:name),person.(:birthdate),person.(:gender),person.(:title)) [new_person.(person.(:name),person.(:birthdate),person.(:gender),person.(:title),id),nil] } get_new_person = -> { handle_result.(add_person.(read_person_from_user.()), ->(person) { puts "Successfully added person #{person.(:id)}" person }, ->(error_message) { puts "Problem: #{error_message}" get_new_person.() } ) }
add_person
अब विशेषता प्राप्त करने के लिए वाक्यविन्यास के कारण कम सुंदर दिखता है, लेकिन अब हम आसानी से नए क्षेत्रों को जोड़ सकते हैं, कार्यक्रम की संरचना को संरक्षित कर सकते हैं।
ऑब्जेक्ट ओरिएंटेशन केवल एक फ़ंक्शन है।
हम व्युत्पन्न क्षेत्र भी बना सकते हैं। मान लीजिए हम उस उपयोगकर्ता के लिए एक अभिवादन जोड़ना चाहते हैं जिसने शीर्षक का संकेत दिया था। हम इसे एक विशेषता बना सकते हैं:
new_person = ->(name,birthdate,gender,title,id) { return ->(attribute) { return id if attribute == :id return name if attribute == :name return birthdate if attribute == :birthdate return gender if attribute == :gender return title if attribute == :title if attribute == :salutation if String(title) == '' return name else return title + " " + name end end nil } }
यदि हम चाहते हैं तो नर्क, हम वास्तविक ओओपी-शैली
विधियाँ जोड़ सकते हैं:
new_person = ->(name,birthdate,gender,title,id) { return ->(attribute) { return id if attribute == :id return name if attribute == :name return birthdate if attribute == :birthdate return gender if attribute == :gender return title if attribute == :title if attribute == :salutation if String(title) == '' return name else return title + " " + name end elsif attribute == :update update_person.(name,birthdate,gender,title,id) elsif attribute == :destroy delete_person.(id) end nil } } some_person.(:update) some_person.(:destroy)
जबकि हम OOP के बारे में बात कर रहे हैं, चलो विरासत में जोड़ते हैं! मान लीजिए हमारे पास एक कर्मचारी है जो मानव है, लेकिन अभी भी एक कर्मचारी संख्या है:
new_employee = ->(name,birthdate,gender,title,employee_id_number,id) { person = new_person.(name,birthdate,gender,title,id) return ->(attribute) { return employee_id_number if attribute == :employee_id_number return person.(attribute) } }
हमने कोड की कुछ पंक्तियों में अकेले कार्यों पर कक्षाएं, ऑब्जेक्ट और वंशानुक्रम बनाए।
एक अर्थ में, एक वस्तु-उन्मुख भाषा में एक वस्तु उन कार्यों का एक समूह है जो एक सामान्य डेटा सेट तक पहुंच है। यह देखना आसान है कि एक कार्यात्मक भाषा में ऑब्जेक्ट सिस्टम को जोड़ना उन लोगों के लिए तुच्छ क्यों माना जाता है जो कार्यात्मक भाषाओं को समझते हैं। ऑब्जेक्ट-ओरिएंटेड भाषा में सुविधाओं को जोड़ने की तुलना में यह बहुत आसान है!
यद्यपि एक्सेसिंग विशेषताओं के लिए वाक्य रचना बहुत सुंदर नहीं है, लेकिन कक्षाओं की कमी मुझे भयानक पीड़ा नहीं देती है। कक्षाएं कुछ गंभीर अवधारणा की तुलना में सिंटैक्टिक चीनी की तरह अधिक दिखती हैं।
हालाँकि, डेटा संशोधन के साथ समस्याएँ हो सकती हैं। देखें कि कैसे
add_person
फ़ंक्शन क्रिया
add_person
। वह
insert_person
में रिकॉर्ड डालने के लिए
insert_person
को कॉल करता है, और उसे आईडी वापस मिल जाता है फिर हमें केवल आईडी सेट करने के लिए एक पूरी तरह से नया रिकॉर्ड बनाने की आवश्यकता है। क्लासिक OOP में, हम सिर्फ
person.id = id
लिखेंगे।
क्या राज्य संक्रमण इस डिजाइन का एक फायदा है? मैं कहूंगा कि इस डिज़ाइन की कॉम्पैक्टीनेस इसका मुख्य लाभ है, और यह तथ्य कि यह ऑब्जेक्ट की परिवर्तनशीलता है जो डिज़ाइन को कॉम्पैक्ट बनाता है, केवल एक दुर्घटना है। केवल अगर हम चरम मेमोरी की कमी और भयानक कचरा संग्रह के साथ एक प्रणाली का उपयोग करते हैं तो हम नई वस्तुओं को बनाने के बारे में चिंतित हो सकते हैं। खरोंच से नई और नई वस्तुओं के बेकार निर्माण से हम वास्तव में नाराज होंगे। लेकिन जब से हम पहले से ही जानते हैं कि फ़ंक्शन, उह, हमारे फ़ंक्शन को कैसे जोड़ना है, तो आइए इस कॉम्पैक्ट सिंटैक्स को लागू करने का प्रयास करें:
new_person = ->(name,birthdate,gender,title,id=nil) { return ->(attribute,*args) { return id if attribute == :id return name if attribute == :name return birthdate if attribute == :birthdate return gender if attribute == :gender return title if attribute == :title if attribute == :salutation if String(title) == '' return name else return title + " " + name end end if attribute == :with_id
अब
add_person
भी सरल है:
add_person = ->(person) { return [nil,"Name is required"] if String(person.(:name)) == '' return [nil,"Birthdate is required"] if String(person.(:birthdate)) == '' return [nil,"Gender is required"] if String(person.(:gender)) == '' return [nil,"Gender must be 'male' or 'female'"] if person.(:gender) != 'male' && person.(:gender) != 'female' id = insert_person.(person.(:name),person.(:birthdate),person.(:gender),person.(:title)) [new_person.(:with_id,id),nil]
यह निश्चित रूप से
person.id = id
रूप में साफ नहीं दिखता है।
person.id = id
, लेकिन यह पढ़ने योग्य होने के लिए पर्याप्त सभ्य दिखता है। इससे कोड बेहतर हो गया।
नाम स्थान केवल कार्य हैं
क्या मैं वास्तव में याद आती है नाम स्थान है। यदि आपने कभी C को प्रोग्राम किया है, तो आप शायद जानते हैं कि नाम के टकराव से बचने के लिए कोड जटिल उपसर्गों के साथ कैसे काम करता है। बेशक, हम यहां कुछ ऐसा ही कर सकते थे, लेकिन सही नामस्थानों के लिए बहुत अच्छे थे, जैसे कि रूबी में मॉड्यूल प्रदान करते हैं या जावास्क्रिप्ट में ऑब्जेक्ट शाब्दिक। मैं भाषा में नई सुविधाओं को जोड़े बिना ऐसा करना चाहूंगा। सबसे आसान तरीका है कि किसी मैपिंग जैसी चीज को लागू किया जाए। हम पहले से ही डेटा संरचना की स्पष्ट विशेषताओं तक पहुंच सकते हैं, इसलिए अब ऐसा करने के लिए अधिक सामान्य तरीके के साथ आने के लिए पर्याप्त है।
फिलहाल, हमारे पास एकमात्र डेटा संरचना एक सरणी है। हमारे पास सरणी विधियां नहीं हैं क्योंकि हमारे पास कक्षाएं नहीं हैं।
रूबी में ऐरे वास्तव में ट्यूपल्स हैं, और हम उन पर प्रदर्शन कर सकते हैं सबसे आम ऑपरेशन डेटा निष्कर्षण है। उदाहरण के लिए:
first = ->((f,*rest)) { f }
हम एक सूची के रूप में प्रदर्शन को मॉडल कर सकते हैं, इसे तीन तत्वों के साथ एक सूची के रूप में देखते हैं: एक कुंजी, एक मूल्य और बाकी प्रदर्शन। चलो "ओओपी शैली" से बचें और केवल शुद्ध "कार्यात्मकता" छोड़ दें:
empty_map = [] add = ->(map,key,value) { [key,value,map] } get = ->(map,key) { return nil if map == nil return map[1] if map[0] == key return get.(map[2],key) }
उपयोग उदाहरण:
map = add.(empty_map,:foo,:bar) map = add.(map,:baz,:quux) get.(map,:foo)
यह नामस्थानों को लागू करने के लिए पर्याप्त है:
people = add.(empty_map ,:insert ,insert_person) people = add.(people ,:update ,update_person) people = add.(people ,:delete ,delete_person) people = add.(people ,:fetch ,fetch_person) people = add.(people ,:new ,new_person) add_person = ->(person) { return [nil,"Name is required"] if String(person.(:name)) == '' return [nil,"Birthdate is required"] if String(person.(:birthdate)) == '' return [nil,"Gender is required"] if String(person.(:gender)) == '' return [nil,"Gender must be 'male' or 'female'"] if person.(:gender) != 'male' && person.(:gender) != 'female' id = get(people,:insert).(person.(:name), person.(:birthdate), person.(:gender), person.(:title)) [get(people,:new).(:with_id,id),nil] }
new_person
, हम प्रदर्शन के
new_person
के कार्यान्वयन को प्रतिस्थापित कर सकते हैं, लेकिन हमारे द्वारा समर्थित विशेषताओं की एक स्पष्ट सूची होना अधिक सुविधाजनक है, इसलिए
new_person
छोड़ दें
new_person
कि यह है।
आखिरी चाल।
include
एक महान रूबी सुविधा है जो आपको मौजूदा दायरे में मॉड्यूल लाने की अनुमति देती है ताकि आप स्पष्ट नाम स्थान रिज़ॉल्यूशन का उपयोग न करें। क्या हम इसे यहाँ कर सकते हैं? बंद:
include_namespace = ->(namespace,code) { code.(->(key) { get(namespace,key) }) } add_person = ->(person) { return [nil,"Name is required"] if String(person.(:name)) == '' return [nil,"Birthdate is required"] if String(person.(:birthdate)) == '' return [nil,"Gender is required"] if String(person.(:gender)) == '' return [nil,"Gender must be 'male' or 'female'"] if person.(:gender) != 'male' && person.(:gender) != 'female' include_namespace(people, ->(_) { id = _(:insert).(person.(:name), person.(:birthdate), person.(:gender), person.(:title)) [_(:new).(:with_id,id),nil] } }
ठीक है, यह पहले से ही बहुत अधिक हो सकता है, लेकिन इसका उपयोग करना अभी भी दिलचस्प है बस कम प्रिंट करने के लिए, जब हम समान व्यवहार को केवल कार्यों का उपयोग करके प्राप्त कर सकते हैं।
हमने क्या सीखा है?
बस कुछ मूल भाषा निर्माण का उपयोग करके, हम एक नई प्रोग्रामिंग भाषा बनाने में सक्षम थे। हम वास्तविक डेटा प्रकार, नाम स्थान बना सकते हैं, और यहां तक कि भाषा निर्माणों द्वारा इसके स्पष्ट समर्थन के बिना OOP का उपयोग कर सकते हैं। और हम कोड की समान मात्रा के साथ ऐसा कर सकते हैं, जैसे कि हम केवल रूबी के अंतर्निहित टूल का उपयोग कर रहे थे। वाक्यविन्यास सामान्य रूबी की तुलना में
थोड़ा अधिक है, लेकिन फिर भी
इतना बुरा नहीं
है । हम रूबी के इस "क्रॉप्ड" संस्करण का उपयोग करके वास्तविक कोड भी लिख सकते हैं। और यह बिल्कुल भयानक नहीं लगेगा।
क्या यह रोजमर्रा के काम में मदद करेगा? मुझे लगता है कि यह सादगी में एक सबक है। रूबी अत्यधिक विशिष्ट निर्माणों, जटिल वाक्यविन्यास और मेटाप्रोग्रामिंग के साथ अतिभारित है, लेकिन हम कक्षाओं का उपयोग किए बिना बहुत कुछ लागू करने में कामयाब रहे! हो सकता है कि आपकी समस्या को सरल तरीके से हल किया जा सके? हो सकता है कि आप सभी "सबसे अच्छे सुविधाओं" का उपयोग करने की कोशिश करने के बजाय भाषा के सबसे स्पष्ट हिस्सों पर भरोसा करें?