मनोरंजक जावास्क्रिप्ट: स्नो डे

छवि


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


पिछले कार्य:



सूत्रीकरण


पिछले एक साल में, सांता क्लॉस ने सामान्य डेवलपर्स के नामों की एक सभ्य सूची एकत्र की है और अब बधाई के लिए एक कार्यक्रम लिखने की योजना बना रहा है। प्रारूप है: happy new year, ${username}! । लेकिन यहां बुरा भाग्य है: कीबोर्ड क्रैश हो जाता है और आपको कई लैटिन वर्णों में प्रवेश करने की अनुमति नहीं देता है। दोष की जांच करने के बाद, कल्पित बौने ने एक दिलचस्प अवलोकन किया कि और क्या काम करता है, आप एक Snowing day जोड़ सकते Snowing day । आउटपुट स्रोत को आपके विवेक पर चुना जा सकता है।

तो, इनपुट पर - गैर-खाली स्ट्रिंग्स के कुछ सरणी (नाम खाली नहीं हो सकता)। केवल लैटिन वर्णों का उपयोग करके एक कार्यक्रम लिखना आवश्यक है: S , n , o , w , i , g , d , a , y (कुल 9 वर्ण, जिनमें से एक ऊपरी मामले में है)। कार्यक्रम को पारित सरणी के चारों ओर जाना चाहिए और प्रत्येक नाम के लिए happy new year, ${username}! उत्पादन करना चाहिए happy new year, ${username}! कुछ प्रकार के आउटपुट स्रोत का उपयोग करना: अलर्ट , कंसोल.लॉग या जो कुछ भी दिमाग में आता है। अच्छा होगा, वैश्विक संदर्भ को प्रदूषित न करें।


आदतन हल


यदि आप कुछ भी आविष्कार नहीं करते हैं, तो सब कुछ बहुत सरल है:


 function happy(users) { for (let i = 0; i !== users.length; i += 1) { console.log(`happy new year, ${users[i]}!`); } } 

या इससे बेहतर:


 function happy(users) { users.forEach(user => console.log(`happy new year, ${user}!`)); } 

हम अपने एरे के साथ उपयोग करते हैं, इसे यूजर्स होने दें:


 let users = ['John', 'Jack', 'James']; happy(users); // happy new year, John! // happy new year, Jack! // happy new year, James! 

लेकिन यहां यह दूर की कौड़ी है: लैटिन कार्यान्वयन में केवल अनुमत पात्रों का उपयोग करने के लिए। अपने दम पर पहले सामना करने की कोशिश करें, और फिर चर्चा में शामिल हों।


मनोरंजक निर्णय


अधीर अभी JSFiddle में नीचे दिए गए समाधान देख सकता है।


समस्या को हल करने के लिए, आपको निम्नलिखित में अतिरिक्त लैटिन से छुटकारा पाने की आवश्यकता है:


  1. एक फ़ंक्शन घोषणा में कीवर्ड फ़ंक्शन
  2. चर घोषित करने के लिए लेट (या संस्करण ) कीवर्ड।
  3. जब एक पास सरणी पर पुनरावृति करने के लिए एक लूप का आयोजन करते हैं तो कीवर्ड।
  4. संदेश के पाठ का गठन।
  5. परिणाम को आउटपुट करने के लिए कुछ फ़ंक्शन को कॉल करें।

एरो फ़ंक्शंस आसानी से पहली समस्या के साथ हमारी मदद करेंगे:


 (arr => el.forEach(/* ... */))(users); 

हम अभी चर नामों पर ध्यान नहीं देंगे, क्योंकि हम आसानी से बहुत ही अंत में उनका नाम बदल सकते हैं।


जहाँ भी किसी फ़ंक्शन या उसके परिणाम की आवश्यकता होती है, हम IIFE के साथ "एरो" का उपयोग करते हैं। इसके अलावा, फ़ंक्शंस आपको दो तरह से लेट और वार्स निर्देशों से छुटकारा पाने की अनुमति देते हैं:


 (param => /* ... */)(value); ((param = value) => /* ... */)(); 

दोनों ही मामलों में, हम फंक्शन पैरामीटर्स में एक वैरिएबल घोषित करते हैं। केवल पहले मामले में हम फ़ंक्शन को कॉल करने पर मान पास करते हैं, और दूसरे में - हम डिफ़ॉल्ट फ़ंक्शन पैरामीटर का उपयोग करते हैं।


दरअसल, समस्याएं तीसरे बिंदु पर शुरू होती हैं। हमारे पास क्लासिक के लिए पर्याप्त वर्ण नहीं हैं, करते हैं , जबकि लूप हैं, न ही for..in और for..of के माध्यम से ट्रैवर्सल विकल्पों के लिए, और न ही सरणी विधियों forEach , मैप , फ़िल्टर के लिए (जहां आप कॉलबैक पास कर सकते हैं)। लेकिन हम एक सरणी पर अपने पुनरावृत्ति फ़ंक्शन को लागू कर सकते हैं:


 function iterate(arr, consume) { function iter(i) { if (arr[i]) { consume(arr[i]); iter(++i); } } iter(0); } 

हम पुनरावर्ती तत्वों को तब तक पार करते हैं जब तक कि वर्तमान स्थिति का सत्यापन बंद नहीं हो जाता। हम यहाँ तार्किक परिवर्तन पर भरोसा क्यों कर सकते हैं? क्योंकि हमारे एरे का तत्व एक खाली स्ट्रिंग नहीं है (केवल यह झूठा लपेटता है ), लेकिन जब हम इंडेक्स इंक्रीमेंट के माध्यम से एरे से बाहर निकलते हैं, तो हम अपरिभाषित हो जाते हैं ( गलत पर कास्ट)।


हम एरो एक्सप्रेशन का उपयोग करके फंक्शन को फिर से लिखते हैं:


 let iterate = (arr, consume) => ( (iter = i => { if (arr[i]) { consume(arr[i]); iter(++i); } }) => iter(0) )(); 

लेकिन हम अगर कथन का उपयोग नहीं कर सकते, क्योंकि हमारे पास वर्ण f नहीं है। हालत को संतुष्ट करने के हमारे कार्य के लिए, हमें इससे छुटकारा पाने की आवश्यकता है:


 let iterate = (arr, consume) => ( (iter = i => arr[i] ? (consume(arr[i]), iter(++i)) : 0) => iter(0) )(); 

टर्नरी ऑपरेटर और कॉमा ऑपरेटर के माध्यम से दो भावों को एक में मिलाने की क्षमता ने इसमें हमारी मदद की। हम इस फ़ंक्शन का उपयोग बाद में समाधान के लेआउट में करेंगे।


चौथी समस्या यह है कि किसी भी मामले में हमें लापता पात्रों के साथ एक स्ट्रिंग प्राप्त करने की आवश्यकता है। जाहिर है, हम वर्णों का प्रतिनिधित्व करने के लिए संख्याओं का उपयोग करेंगे। कई विकल्प हैं:


  • एक String.fromCharCode फ़ंक्शन जो पूर्णांक संख्या को लौटने और निर्दिष्ट यूनिकोड अनुक्रम से बनाए गए स्ट्रिंग को वापस करने की अपेक्षा करता है।
  • \uhhhh सीक्वेंस \uhhhh निर्दिष्ट हेक्साडेसिमल कोड का उपयोग करके किसी भी यूनिकोड चरित्र का उत्पादन करने की अनुमति देता है।
  • प्रारूप &#dddd; HTML- वर्णों के लिए आप निर्दिष्ट दशमलव कोड का उपयोग करके पेज दस्तावेज़ में एक प्रतीक प्रदर्शित कर सकते हैं।
  • संख्या प्रोटोटाइप ऑब्जेक्ट के स्ट्रिंग फ़ंक्शन में एक अतिरिक्त मूलांक पैरामीटर है - संख्या प्रणाली का आधार।
  • शायद कुछ और भी हो ...

आप पहले तीन विकल्पों की दिशा में खुद को खोद सकते हैं, और अब इस कार्य के लिए शायद सबसे आसान एक पर विचार करें: Number.prototyp.stStringमूलांक पैरामीटर का अधिकतम मान 36 है (10 अंक + 26 लोअरकेस लैटिन वर्ण):


 let symb = sn => (sn + 9).toString(36); 

इस प्रकार, हम वर्णमाला में संख्या के आधार पर कोई भी लैटिन वर्ण प्राप्त कर सकते हैं, जिसकी शुरुआत 1. केवल यही है कि सभी वर्ण निचले हैं। हां, यह हमारे लिए संदेश में पाठ प्रदर्शित करने के लिए पर्याप्त है, लेकिन हम कुछ तरीकों और कार्यों (समान के लिए) को जोड़ नहीं सकते हैं।


लेकिन यह आनन्दित करने के लिए बहुत जल्दी है, पहले आपको फ़ंक्शन प्रविष्टि में स्ट्रिंग से छुटकारा पाने की आवश्यकता है। सबसे पहले, हम इस विधि की ओर मुड़ते हैं:


 let symb = sn => (sn + 9)['toString'](36); 

यदि आप बारीकी से देखते हैं, तो स्ट्रिंग के लिए हमें केवल दो वर्णों की आवश्यकता है: t और r : बाकी सभी शब्द Snowing । उन्हें प्राप्त करना बहुत सरल है, क्योंकि उनका आदेश पहले से ही true । निहित प्रकार के रूपांतरणों का उपयोग करते हुए, हम इस स्ट्रिंग और उन वर्णों को प्राप्त कर सकते हैं जिनकी हमें आवश्यकता है:


 !0+''; // 'true' (!0+'')[0]; // 't' (!0+'')[1]; // 'r' 

हम किसी भी लैटिन पत्र को प्राप्त करने का कार्य करते हैं:


 let symb = sn => (sn + 9)[(!0+'')[0] + 'oS' + (!0+'')[0] + (!0+'')[1] + 'ing'](36); 

सिम्ब का उपयोग करके अक्षर अनुक्रम संख्याओं के एक शब्द से शब्द प्राप्त करने के लिए, हम मानक Array.prototype.reduce फ़ंक्शन का उपयोग करते हैं:


 [1,2,3].reduce((res, sn) => res += symb(sn), ''); // 'abc' 

हाँ, यह हमें शोभा नहीं देता। लेकिन हमारे समाधान में, हम पुनरावृति फ़ंक्शन के साथ कुछ ऐसा कर सकते हैं:


 let word = chars => (res => (iterate(chars, ch => res += symb(ch)), res))(''); word([1,2,3]); // 'abc' 

चौकस लोग इस बात पर ध्यान देंगे कि हमने स्ट्रिंग की एक सरणी के लिए पुनरावृति फ़ंक्शन विकसित किया है, लेकिन हम इसका उपयोग यहां संख्याओं के साथ करते हैं। यही कारण है कि हमारे वर्णमाला का प्रारंभिक सूचकांक 1 है, न कि 0. अन्यथा, एक इंप्रोमेप्टु चक्र तब समाप्त होता है जब यह 0 (अक्षर a ) से मिलता था।


पात्रों को उनके सीरियल नंबर पर मैप करने में आसानी के लिए, आप एक शब्दकोष प्राप्त कर सकते हैं:


 [...Array(26).keys()].reduce((map, i) => (map[symb(i + 1)] = i + 1, map), {}); // {a: 1, b: 2, c: 3, d: 4, e: 5, …} 

लेकिन यह और भी सरल करने के लिए समझदार है और उलटे शब्द परिवर्तन के कार्य को संपूर्ण रूप में लिखता है:


 let reword = str => str.split('').map(s => parseInt(s, 36) - 9); reword('happy'); // [8,1,16,16,25] reword('new'); // [14,5,23] reword('year'); // [25,5,1,18] 

हम स्वयं संदेश बनाने का कार्य पूरा करते हैं:


 let message = name => word([8,1,16,16,25]) + ' ' + word([14,5,23]) + ' ' + word([25,5,1,18]) + ', ' + name + '!'; 

पांचवीं समस्या के निष्कर्ष से निपटने के लिए बहुत कम बचा है। कंसोल , अलर्ट , कन्फर्मेशन , प्रॉम्प्ट , इनरएचटीएमएल , डॉक्यूमेंट.राइट मन में आते हैं। लेकिन सूचीबद्ध विकल्पों में से कोई भी सीधे नहीं पहुँचा जा सकता है।


हमें शब्द फ़ंक्शन का उपयोग करके किसी भी शब्द को प्राप्त करने का अवसर भी मिला। इसका मतलब यह है कि हम वस्तुओं से कई कार्यों को वर्ग कोष्ठक के माध्यम से एक्सेस करके कॉल कर सकते हैं, जैसा कि स्ट्रींग के मामले में था।


यह देखते हुए कि हम तीर फ़ंक्शन का उपयोग करते हैं, यह संदर्भ वैश्विक बना हुआ है (और इसे अग्रेषित करने की कोई आवश्यकता नहीं है)। किसी भी स्थान पर, हम एक पंक्ति के माध्यम से इसके कई कार्य कर सकते हैं:


 this[word([1,12,5,18,20])]('hello'); // alert('hello'); this[word([3,15,14,19,15,12,5])][word([12,15,7])]('hello'); // console.log('hello'); 

लेकिन इसके "ड्राइंग" के this हमारे पास फिर से पात्रों की कमी है। हम इसे Window.self से बदल सकते हैं, लेकिन इसके साथ उपलब्ध वर्णमाला के संदर्भ में और भी बुरा है। हालांकि, यह खिड़की की वस्तु पर ही ध्यान देने योग्य है, जिसकी "रूपरेखा" हमारे लिए काफी संतोषजनक है, भले ही वह बकरी हो, और बहुत लंबा हो!


वैसे, कार्य के पहले संस्करण में, कुंजी वाक्यांश केवल Snowing शब्द था, और window को तह नहीं किया जा सकता था ( d चरित्र की अनुपस्थिति के कारण)। संदर्भ तक पहुंच jsfuck चाल में से एक पर आधारित थी:


 (_ => 0)['constructor']('return this')()['alert']('hello'); 

या आप किसी वैश्विक संदर्भ में भी सीधे पहुंच सकते हैं:


 (_ => 0)['constructor']('return alert')()('hello'); 

जैसा कि आप देख सकते हैं, उदाहरणों में पूरा लैटिन लाइनों में है। यहां हम एक स्ट्रिंग से एक फंक्शन बनाते हैं, और हम फंक्शन (कंस्ट्रक्टर) तक पहुंच जाते हैं। लेकिन यह किसी भी तरह बहुत ज्यादा है! शायद किसी और को पता है कि हमारी स्थितियों में संदर्भ का उपयोग कैसे किया जाए?


अंत में, हमने इसे एक साथ रखा! हमारे "मुख्य" फ़ंक्शन का शरीर उत्तीर्ण सरणी के लिए पुनरावृति कहेगा , और उपभोक्ता पहले से निर्मित संदेश संकलन फ़ंक्शन के परिणाम को आउटपुट करेगा। संदेश और आदेशों के पाठ के लिए, एक शब्द फ़ंक्शन का उपयोग किया जाता है, जिसे पुनरावृति की भी आवश्यकता होती है, और हम इसे डिफ़ॉल्ट मापदंडों के बगल में निर्धारित करेंगे। इस तरह:


 (users => ( (( // firstly we need the iterating function iterate = (array, consume) => ((iter = i => array[i] ? (consume(array[i]), iter(++i)) : 0) => iter(0))(), // then we determine the word-creating function word = chars => (res => (iterate(chars, ch => res += (ch + 9)[(!0+'')[0] + 'oS' + (!0+'')[0] + (!0+'')[1] + 'ing'](36) ), res) )('') ) => iterate(users, name => // using console.log in window for printing out window[word([3,15,14,19,15,12,5])][word([12,15,7])]( word([8,1,16,16,25]) + ' ' + word([14,5,23]) + ' ' + word([25,5,1,18]) + ', ' + name + '!' ) ))() ))(users); 

अनुमत वर्णमाला का उपयोग करके चर का नाम बदलें:


 (_10 => ( (( _123 = (ann, snow) => ((_12 = i => ann[i] ? (snow(ann[i]), _12(++i)) : 0) => _12(0))(), wo = ann => (w => (_123(ann, an => w += (an + 9)[(!0+'')[0] + 'oS' + (!0+'')[0] + (!0+'')[1] + 'ing'](36) ), w) )('') ) => _123(_10, _1 => window[wo([3,15,14,19,15,12,5])][wo([12,15,7])]( wo([8,1,16,16,25]) + ' ' + wo([14,5,23]) + ' ' + wo([25,5,1,18]) + ', ' + _1 + '!' ) ))() ))(users); 

परिवर्तनशीलविवरण
_123 {function}सरणी के तत्वों पर पुनरावृति करने के लिए पुनरावृति कार्य करता है।
_12 {function}स्थानीय पुनरावृति कार्य जो पुनरावृत्ति को पुनरावृति कहता है।
बर्फ {function}फ़ंक्शन को पुनरावृति के लिए कॉलबैक के रूप में खपत करें
ऐन {Array<Any>}एरियर पैरामीटर।
{Any}एरे तत्व तत्व।
wo {function}शब्द बनाने का कार्य।
w {string}शब्द में एक स्ट्रिंग जमा करने के लिए स्थानीय चर।
_10 {Array<string>}उपयोगकर्ताओं की मूल सरणी।
_1 {string}स्रोत सरणी से उपयोगकर्ता, उसका नाम।

वह सब है। इस बारे में अपने विचार और विचार लिखें, क्योंकि कुछ अलग करने या न करने के कई विकल्प हैं।


निष्कर्ष


यह दिलचस्प है कि समस्या की स्थितियों के लिए एक शब्द या वाक्यांश के साथ आने के लिए एक वास्तविक परीक्षा हुई। मैं चाहता था कि वह कम और बहुत विचारोत्तेजक न हो, और अधिक या कम संक्षिप्त समाधान के लिए उपयुक्त हो।


इस कार्य के लिए प्रेरणा जावास्क्रिप्ट की कार्यक्षमता और बहुत से ज्ञात 6 वर्णों के आइसोट्रिक द्वारा प्रदान की गई थी। जैसा कि पहले चर्चा किए गए कार्यों में, इस विषय पर कई भिन्नताएं हो सकती हैं, और एकमात्र समाधान नहीं। यह एक साधारण शब्द और एक प्रमुख वाक्यांश के साथ आने के लिए पर्याप्त है। नए साल में मिलते हैं!

Source: https://habr.com/ru/post/hi433112/


All Articles