कार्य कम करें

हाल के वर्षों में जावास्क्रिप्ट ने गंभीर लोकप्रियता हासिल की है, और इसलिए इसके नुकसान भी स्पष्ट रूप से दिखाई देने लगे हैं। निष्पक्षता में, यह ध्यान देने योग्य है कि किसी भी भाषा में कुछ हद तक अपनी विरासत और नुकसान दोनों हैं।

विशेष रूप से, जावास्क्रिप्ट में पत्थरों का एक पूरा बगीचा है। पानी के नीचे का बगीचा।



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



मुझे प्रश्न संख्या 3 में दिलचस्पी थी:

इस अभिव्यक्ति (या कई) का परिणाम क्या है?



[ [3,2,1].reduce(Math.pow), [].reduce(Math.pow) ]
      
      





एक उत्तर के रूप में, लेखकों, पसंद पर, निम्नलिखित विकल्प दिए गए हैं:

* त्रुटि

* [9, 0]





* [9, NaN]





* [9, undefined]







इस प्रश्न का उत्तर देने के लिए केवल दुभाषिया का उपयोग करते हुए, दुभाषिए को शुरू किए बिना, इसे आज़माएं



इस तथ्य के बावजूद कि उदाहरण काफी दूरस्थ है, फ़ंक्शन और आंशिक रूप से परिभाषित कार्यों के संग्रह के लिए आवेदन जेएस के लिए एक सामान्य अभ्यास है, और, सामान्य उपयोग के साथ, यह कोड क्लीनर बनाने में सक्षम है, दोनों निष्पादन के मामले में - इसे अनावश्यक बंद होने से बचाने के लिए, और दृश्य में योजना - कम ब्रैकेट कचरा (प्रीप्रोसेसरों का उपयोग करने का प्रश्न दूसरे लेख के लिए छोड़ दिया गया है)।



और इस लेख में आप पाएंगे:

* कार्यों का विश्लेषण।

* जावास्क्रिप्ट विशुद्ध रूप से व्यावहारिक दृष्टिकोण से reduce



हो जाती है।

* reduce



साथ कई शैक्षणिक अध्ययन (देखने के एक अकादमिक बिंदु से reduce



)।

* लेख के लिए बन्स के साथ भंडार।

* कई अन्य reduce







टास्क पार्सिंग



ठीक है, के साथ शुरू करने के लिए, हम लेख की शुरुआत में कार्य से निपटेंगे। और पर्याप्त विकल्प हैं।

reduce



(इसके बाद Array.prototype.reduce



रूप में Array.prototype.reduce



किया जाता है), Array



प्रोटोटाइप के अन्य कार्यों के साथ: filter



, map



, forEach



, some



, every



, एक उच्च-क्रम फ़ंक्शन है, अर्थात, यह इनपुट के लिए एक और फ़ंक्शन लेता है (हम इसे पारित कहेंगे) समारोह f*



)। संग्रह में प्रत्येक आइटम के लिए f*



फ़ंक्शन को कुछ तर्कों के साथ बुलाया जाएगा।



विशेष रूप से, संग्रह के आधार पर कुछ कुल मूल्य उत्पन्न करने के लिए reduce



किया जाता है। यह प्रत्येक तत्व पर क्रमिक रूप से f*



लागू होता है, इसे चर के वर्तमान मूल्य को पारित करना जिसमें परिणाम (बैटरी) और वर्तमान तत्व जमा हो रहे हैं। इसके अलावा, reduce



आप बैटरी के प्रारंभिक मूल्य को पारित कर सकते हैं। इसके अलावा, (!) इस मान के पारित होने या न होने के आधार पर व्यवहार reduce



हो जाएगा



Math.pow



फ़ंक्शन Math.pow



करता है, अर्थात, इसका व्यवहार पास की गई डिग्री के आधार पर भिन्न होता है : यह एक वर्ग, घन या वर्गमूल, या कोई अन्य वास्तविक डिग्री हो सकता है।



निम्नलिखित प्रश्न खुले रहते हैं:

* खाली सरणी पर कॉल करने पर व्यवहार reduce



कैसे हो जाता है?

* यदि वह अपनी डिग्री पूरी नहीं करता है तो Math.pow



कैसे व्यवहार करता है?



मानक JS कार्यों के लिए कोई सामान्य त्रुटि हैंडलिंग नीति नहीं है। कुछ फ़ंक्शन सख्ती से कार्य कर सकते हैं: यदि अपवादित डेटा में कुछ गलत है, तो अपवाद को फेंक दें, कुछ सभी प्रकार के खाली मान लौटाएंगे: null



, undefined



, NaN



, जबकि अन्य अनुमेय रूप से काम करेंगे: वे काफी डेटा डेटा के साथ भी कुछ करने की कोशिश करेंगे।



सिर्फ एक उदाहरण से कितने सवाल खड़े हो गए।



और अब सही उत्तर: हमें एक TypeError



मिलता है जिसमें दूसरा उपप्रकार दोष है। एक खाली सरणी पर और बिना एक प्रारंभिक प्रारंभिक मान के फ़ंक्शन reduce



जाता है।



ऐसा क्यों? reduce



के विनिर्देश पढ़ें



ठीक है, आइए पढ़ते हैं कि MDN Array.prototyp.reduce के बारे में क्या लिखता है । फ़ंक्शन की निम्नलिखित सूक्ष्मताएँ स्पष्ट की गई हैं:

यदि initialValue



पारित हो गया है, तो पहले पुनरावृत्ति पर, फ़ंक्शन को इस मान और सरणी के पहले तत्व के मूल्य के साथ बुलाया जाएगा। यदि initialValue



पारित नहीं किया गया है, तो फ़ंक्शन को सरणी के पहले और दूसरे तत्वों के मूल्यों के साथ बुलाया जाएगा। यह भी अनुसरण करता है कि यदि प्रारंभिक मान पारित नहीं किया जाता है, तो फ़ंक्शन को एक कम समय कहा जाता है , अन्यथा बिल्कुल कई बार जैसा कि सरणी में तत्व होते हैं।



आप इस तरह initialValue



साथ एक फार्म जमा कर सकते हैं:



 array.reduce(fn, initialValue) ⇔ [ initialValue ].concat(array).reduce(fn);
      
      





दूसरा दिलचस्प पहलू एक खाली सरणी को संभाल रहा है। यदि सरणी खाली है और प्रारंभिक मान पारित किया गया है, तो यह फ़ंक्शन का परिणाम है, और परिणाम f*



अनदेखा किया गया है । यदि सरणी खाली है और प्रारंभिक मान पारित नहीं हुआ है, तो एक TypeError



फेंका जाता है।



 [].reduce(fn, initialValue) ⇔ [ initialValue ].reduce(fn) ⇒ initialValue; [].reduce(fn) ⇒ TypeError;
      
      





वास्तव में, फ़ंक्शन का व्यवहार काफी तार्किक है: यह इनपुट डेटा से मूल्यों के साथ f*



कॉल करने की कोशिश करता है। यदि प्रारंभिक मान पारित किया गया है, तो यह एक डेटा तत्व है जो पहले तत्व से पहले है। यदि कुछ भी प्रेषित नहीं किया जाता है (कोई तत्व और प्रारंभिक मूल्य नहीं हैं), तो फ़ंक्शन के पास कुल उत्पन्न करने के लिए कोई डेटा नहीं है, और यह एक अपवाद फेंकता है। एक तरह से या किसी अन्य, व्यवहार थोड़ा जटिल है और एक गड्ढा बन सकता है। reduce



, वास्तव में, एक तर्क के लिए और दो के लिए अतिभारित है, और अतिभारित विकल्पों में खाली सरणी पर अलग व्यवहार होता है।



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

* जेडी: दिमाग में कोड निष्पादित करें, यह जानते हुए कि कैसे reduce



और Math.pow





* चरवाहा: इस कोड को REPL में चलाएं और परिणाम के लिए तर्क को संक्षेप में प्रस्तुत करने का प्रयास करें।



इसके अलावा, आप अपने उदाहरण से खुद को परिचित कर सकते हैं, जिससे समस्या को समझने में मदद मिलेगी:

StreetStrider / habrahabr- जावास्क्रिप्ट-कम करें: परीक्षण / puzzler.js । यह चमेली की परीक्षा है।



जादू और आकर्षण reduce





reduce



उल्लेखनीय है कि इसका उपयोग Array



ऑब्जेक्ट के अन्य सभी उच्च-क्रम कार्यों का वर्णन करने के लिए किया जा सकता है: forEach



, filter



, map



, some



, every







यह स्पष्ट हो जाएगा यदि आप इस विचार से छुटकारा पा लेते हैं कि reduce



करने के लिए सरणी में मानों के समान प्रकार के मूल्य को जमा करना आवश्यक है। दरअसल, यह सोचना तर्कसंगत है कि यदि हम संख्याओं को लेते हैं और उन्हें जोड़ते हैं, तो हमें एक संख्या भी मिलती है। यदि हम स्ट्रिंग्स की एक सरणी लेते हैं और उन्हें संक्षिप्त करते हैं, तो हम स्ट्रिंग भी प्राप्त करते हैं। यह स्वाभाविक है, लेकिन एरे और वस्तुओं को वापस reduce



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



करने की अनुमति देता है।



उदाहरण के लिए, चलो एक map



बनाएँ:



 function map$viaReduce (array, fn) { return array.reduce(function (memo, item, index, array) { return memo.concat([ fn(item, index, array) ]); }, []); };
      
      





यहां, संचित सरणी को बैटरी के माध्यम से प्रेषित किया जाता है। यह मूल के समान आकार होगा, लेकिन ट्रांसफार्मर फ़ंक्शन fn



माध्यम से पारित मूल्यों के साथ। इसके अलावा, यह भूल नहीं है, fn



न केवल तत्व को स्वीकार करता है, बल्कि निम्न मापदंडों के साथ सूचकांक और सरणी। अगर fn



एरे लौटाता है तो concat



फंक्शन concat



पैरामीटर वैल्यू के "एक्सपेंशन" से बचने के लिए एक अरै में लिपटे रहता है। एक खाली सरणी को प्रारंभिक मूल्य के रूप में पारित किया जाता है।



यह कोड रिपॉजिटरी में है, और इसके लिए परीक्षण हैं।



जो लोग रुचि रखते हैं, उनके लिए मैं व्यायाम के रूप में filter



कार्यों को लागू करने का सुझाव देता हूं, और मात्रात्मक कार्यों में से एक: some



या every



। आप देखेंगे कि हर जगह संचित सरणी की वापसी का उपयोग किया जाता है।



एक और गैर-तुच्छ उदाहरण जो दिमाग में आता है, वह uniq



फ़ंक्शन का कार्यान्वयन है। जैसा कि आप जानते हैं, मानक मानक में जावास्क्रिप्ट कई आवश्यक चीजों की कमी से ग्रस्त है। विशेष रूप से, ऐसा कोई फ़ंक्शन नहीं है जो सरणी में डुप्लिकेट को समाप्त करता है, और डेवलपर्स विभिन्न कस्टम कार्यान्वयन का उपयोग करते हैं (मैं व्यक्तिगत रूप से LoDash / अंडरस्कोर से _.uniq का उपयोग करने की सलाह देता हूं)।



यह कार्यान्वयन थोड़ा " हिपस्टर " है, लेकिन reduce



विकल्पों के उदाहरण के रूप में, यह reduce





 function uniq$viaReduce (array) { return array.reduce(function (memo, item) { return (~ memo.indexOf(item) ? null : memo.push(item)), memo; }, []); };
      
      





यहां हम टर्नरी ऑपरेटर के अंदर साइड इफेक्ट का उपयोग करते हैं, अर्थात्, हम तत्व को सरणी में धक्का देते हैं यदि यह वर्तमान टुकड़े पर नहीं मिला है। टिल्ड ऑपरेटर का उपयोग -1



साथ तुलना करने के लिए किया जाता है। पूरी अभिव्यक्ति एक अल्पविराम ऑपरेटर में लिपटी है, जो प्रत्येक चरण पर (सभी कार्यों के बाद) memo



देता है। विशेष रूप से, यह कार्यान्वयन सरणी में आदेश भी बनाए रखता है।



कोड और परीक्षण भंडार में हैं।



ठीक है, "थोड़ा" नहीं, यह कोड बहुत अजीब था, यह मुझे परीक्षण करने के लिए उचित ठहराता है और यह एक पुस्तकालय प्रकार का कार्य है, जिसका व्यवहार नहीं बदलेगा। अपने कोड में अन्य कार्यान्वयन का उपयोग करने की सलाह दी जाती है, जबकि reduce



और indexOf



दोनों का उपयोग करते हुए इस तरह के uniq



के प्रदर्शन को नकारात्मक रूप से प्रभावित करेगा, और एकल-लाइन और टिल्ड्स का प्रचुर उपयोग पठनीयता को प्रभावित करेगा।



वार्म-अप के रूप में, मैं लागू करने की सलाह देता हूं, उदाहरण के लिए, zipObject



फ़ंक्शन, zipObject



सार यह है कि यह जोड़े (सरणियों) के एक सरणी का इनपुट लेता है, जहां शून्य तत्व कुंजी है और पहला मान है, और निर्मित Object



को संबंधित कुंजियों / मानों के साथ लौटाता है।



भंडार के बारे में अधिक जानें।



उदाहरण रिपॉजिटरी एक npm पैकेज है। इसे github पर पते का उपयोग करके सेट किया जा सकता है:

 npm install StreetStrider/habrahabr-javascript-reduce
      
      





src/



कार्य के उदाहरण हैं, tests/



चमेली परीक्षण हैं। आप npm test



परीक्षण का उपयोग करके सभी परीक्षण चला सकते हैं।



रिपॉजिटरी में एक डिग्री (और अन्य सीमा मामलों) की अनुपस्थिति में Math.pow



के अर्थ के बारे में सवाल का जवाब भी है।



अन्य reduce





* जावास्क्रिप्ट में, reduce



एक बुरा जुड़वां भाई है, एक दाहिने हाथ एनालॉग: reduceRight



। महंगा reverse



की आवश्यकता के बिना, दाएं से बाएं ओर एग्रीगेट करना आवश्यक है।

* LoDash / अंडरस्कोर _.reduce



, _.reduceRight



। उनके पास कई अतिरिक्त विशेषताएं हैं।

* अजगर ने reduce



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



निर्माणों का उपयोग करने का प्रस्ताव है। अधिक कोड प्राप्त किया जाता है, लेकिन यह बहुत अधिक पठनीय हो जाता है। यह ताओ भाषा से मेल खाती है।

* कुछ भाषाओं में, reduce/reduceRight



को foldl/foldr



कहा जाता है।



SQL में पाँच मानक समुच्चय हैं: COUNT



, SUM



, AVG



, MAX



, MIN



। इन कार्यों का उपयोग एकल ट्यूपल के परिणामी चयन को कम करने के लिए किया जाता है। इसी तरह के कार्यों को जावास्क्रिप्ट में ( reduce



पर भी) लागू किया जा सकता है।



वैसे, पांच में से चार एग्रीगेटिंग एसक्यूएल फ़ंक्शंस (काउंट COUNT



नहीं कर रहे हैं) यदि चयन खाली है तो NULL



वापस लौटें ( COUNT



एक निश्चित मान लौटाता है: 0



)। यह पूरी तरह से एक खाली सूची पर JS TypeError



अनुरूप है।



 postgres=# SELECT SUM(x) FROM (VALUES (1), (2), (3)) AS R(x); sum ----- 6 (1 row)
      
      





 postgres=# SELECT SUM(x) IS NULL AS sum FROM (VALUES (1), (2), (3)) AS R(x) WHERE FALSE; sum ----- t (1 row)
      
      





परिणाम



reduce



करना एक शक्तिशाली कार्य है जिसके संदर्भ में अन्य उच्च-क्रम के कार्यों, जैसे कि map



और filter



को व्यक्त किया जा सकता है। इस शक्ति के साथ, जटिलता reduce



reduce



को विभिन्न योगों और समूहों के लिए सफलतापूर्वक उपयोग किया जा सकता है, हालांकि, यह याद रखने योग्य है कि किसी भी एकत्रीकरण को एक नियमित लूप का उपयोग करके फिर से लिखा जा सकता है, जो पढ़ने में आसान हो सकता है।



संदर्भ

  1. जावास्क्रिप्ट गूढ़ व्यक्ति
  2. MDN: Array.prototyp.reduce ()
  3. गिथब: स्ट्रीटस्ट्राइडर / हैब्राहाब-जावास्क्रिप्ट-कम
  4. JavaScript.ru: ऐरे: तरीकों पर विचार करना


धन्यवाद



मुझे लगता है कि reduce



कुछ भी वापस कर सकते हैं बनाने के लिए subzey के लिए धन्यवाद।

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



आपका ध्यान देने के लिए धन्यवाद।



All Articles