2010-06-30 10 views
111

मैं reduce और apply के बीच वैचारिक अंतर को समझने:Clojure: को कम बनाम लागू

(reduce + (list 1 2 3 4 5)) 
; translates to: (+ (+ (+ (+ 1 2) 3) 4) 5) 

(apply + (list 1 2 3 4 5)) 
; translates to: (+ 1 2 3 4 5) 

हालांकि, जो एक और मुहावरेदार clojure है? क्या यह एक तरफ या दूसरे में बहुत अंतर बनाता है? मेरे (सीमित) प्रदर्शन परीक्षण से, ऐसा लगता है कि reduce थोड़ा तेज़ है।

उत्तर

107

reduce और apply केवल (लौटे अंतिम परिणाम के संदर्भ में) साहचर्य कार्य करता है जो चर-arity मामले में उनके सभी तर्क देखने की जरूरत के लिए बराबर निश्चित रूप से कर रहे हैं। जब वे परिणामस्वरूप समकक्ष होते हैं, तो मैं कहूंगा कि apply हमेशा पूरी तरह से मूर्खतापूर्ण है, जबकि reduce समकक्ष है - और कई आम मामलों में - आंखों के झपकी के एक अंश को बंद कर सकता है। इस पर विश्वास करने के लिए मेरा तर्क क्या है।

+ वैरिएबल-आर्टी केस (2 से अधिक तर्क) के लिए reduce के संदर्भ में ही लागू किया गया है। दरअसल, यह किसी भी परिवर्तनीय-arity, सहयोगी फ़ंक्शन के लिए जाने के लिए बेहद समझदार "डिफ़ॉल्ट" तरीका प्रतीत होता है: reduce में चीजों को गति देने के लिए कुछ अनुकूलन करने की क्षमता है - शायद internal-reduce जैसे कुछ के माध्यम से, एक 1.2 नवीनता हाल ही में मास्टर में अक्षम , लेकिन आशा है कि भविष्य में फिर से पेश किया जाए - जो कि हर काम में दोहराने के लिए मूर्खतापूर्ण होगा जो वेरगास मामले में उनसे लाभ उठा सकता है। ऐसे सामान्य मामलों में, apply बस थोड़ा ओवरहेड जोड़ देगा। (ध्यान दें कि इसके बारे में वास्तव में चिंतित होने के लिए कुछ भी नहीं है।)

दूसरी ओर, एक जटिल कार्य कुछ अनुकूलन अवसरों का लाभ उठा सकता है जो reduce में निर्मित होने के लिए सामान्य नहीं हैं; तो apply आपको उन लोगों का लाभ लेने देगा, जबकि reduce वास्तव में आपको धीमा कर सकता है। अभ्यास में होने वाले बाद के परिदृश्य का एक अच्छा उदाहरण str द्वारा प्रदान किया गया है: यह StringBuilder आंतरिक रूप से उपयोग करता है और के बजाय apply के उपयोग से महत्वपूर्ण रूप से लाभान्वित होगा।

तो, मैं संदेह में apply का उपयोग कहूंगा; और यदि आपको पता है कि यह आपको reduce से अधिक कुछ नहीं खरीद रहा है (और यह कि जल्द ही बदलने की संभावना नहीं है), तो reduce का उपयोग करने के लिए स्वतंत्र महसूस करें, यदि आप इसे महसूस करते हैं तो उस कम अनावश्यक ओवरहेड को दाढ़ी दें।

+0

ग्रेट उत्तर। एक तरफ ध्यान दें, क्यों हैकेल में अंतर्निर्मित 'sum' फ़ंक्शन शामिल नहीं है? एक सुंदर आम ऑपरेशन की तरह लगता है। – dbyrne

+14

धन्यवाद, यह सुनकर खुशी हुई! पुन: 'sum', मैं कहूंगा कि क्लोजर में यह फ़ंक्शन है, इसे' + 'कहा जाता है और आप इसे 'लागू' के साथ उपयोग कर सकते हैं। :-) गंभीरता से कहा जाए तो मुझे लगता है कि लिस्प में सामान्य रूप में, एक variadic समारोह प्रदान की जाती है, तो यह आम तौर पर एक आवरण संग्रह पर काम के साथ नहीं कर रहा है - कि क्या आप (या 'reduce' के लिए apply' का उपयोग', यदि आप जानते है जो अधिक समझ में आता है)। –

+4

मजेदार, मेरी सलाह विपरीत है: संदेह होने पर 'कम करें', जब आप जानते हैं कि अनुकूलन है तो 'लागू करें'। 'Reduce' के अनुबंध और अधिक सटीक और इस प्रकार अधिक सामान्य अनुकूलन की संभावना है। 'Apply' अधिक अस्पष्ट है और इस प्रकार केवल एक मामला-दर-मामला आधार पर अनुकूलित किया जा सकता है। 'Str' और' concat' दो प्रचलित exceptons हैं। – cgrand

13

इससे इस मामले में कोई फर्क नहीं पड़ता है, क्योंकि + एक विशेष मामला है जो किसी भी तर्क पर लागू हो सकता है। कमी एक ऐसे कार्य को लागू करने का एक तरीका है जो तर्क की मनमाने ढंग से लंबी सूची में निश्चित संख्या में तर्क (2) की अपेक्षा करता है।

9

मैं आमतौर पर किसी भी तरह के संग्रह पर अभिनय करते समय खुद को कम करना पसंद करता हूं - यह अच्छा प्रदर्शन करता है, और सामान्य रूप से यह एक बहुत उपयोगी काम है।

मुख्य कारण मैं लागू करने का उपयोग करता हूं यह है कि यदि पैरामीटर अलग-अलग स्थितियों में अलग-अलग चीजें हैं, या यदि आपके पास कुछ शुरुआती पैरामीटर हैं लेकिन बाकी संग्रह को प्राप्त करना चाहते हैं, उदा।

(apply + 1 2 other-number-list) 
18

राय भिन्न होती है- अधिक लिस्प दुनिया में, reduce निश्चित रूप से अधिक मूर्खतापूर्ण माना जाता है। सबसे पहले, पहले से चर्चा की गई विविधतापूर्ण समस्याएं हैं। साथ ही, कुछ सामान्य लिस्प कंपाइलर असफल हो जाएंगे जब apply बहुत लंबी सूचियों के खिलाफ लागू होता है क्योंकि वे तर्क सूचियों को कैसे प्रबंधित करते हैं।

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

41

इस उत्तर को देख newbies के लिए,
सावधान रहना होगा, वे ही नहीं हैं:

(apply hash-map [:a 5 :b 6]) 
;= {:a 5, :b 6} 
(reduce hash-map [:a 5 :b 6]) 
;= {{{:a 5} :b} 6} 
+0

उत्कृष्ट उदाहरण, धन्यवाद! =) – sova

5

इस विशिष्ट मामले में मैं reduce पसंद करते हैं क्योंकि यह अधिक है पठनीय: जब मैं

(reduce + some-numbers) 
पढ़

मुझे पता है कि आप एक अनुक्रम को एक मूल्य में बदल रहे हैं।

apply के साथ मुझे यह विचार करना होगा कि कौन सा फ़ंक्शन लागू किया जा रहा है: "आह, यह + फ़ंक्शन है, इसलिए मुझे ... एक नंबर मिल रहा है"। थोड़ा कम सीधा।

4

+ जैसे साधारण कार्य का उपयोग करते समय, इससे कोई फ़र्क नहीं पड़ता कि आप किस का उपयोग करते हैं।

आम तौर पर, विचार यह है कि कम करना एक संचय ऑपरेशन है। आप अपने संचय समारोह में वर्तमान संचय मूल्य और एक नया मान प्रस्तुत करते हैं, जिसके परिणामस्वरूप अगले पुनरावृत्ति के लिए संचयी मूल्य होता है। तो, अपने पुनरावृत्तियों की तरह लग रहे:

cum-val[i+1] = F(cum-val[i], input-val[i]) ; please forgive the java-like syntax! 

के लिए लागू होते हैं, यह विचार है कि आप एक समारोह अदिश तर्क के एक नंबर की उम्मीद कर कॉल करने के लिए प्रयास कर रहे हैं है, लेकिन वे एक संग्रह में हैं और बाहर निकाला जाना चाहिए। तो, बजाय कह रही:

vals = [ val1 val2 val3 ] 
(some-fn (vals 0) (vals 1) (vals2)) 

हम कह सकते हैं:

(apply some-fn vals) 

और यह के बराबर होने का बदल जाती है: "लागू करें" का उपयोग कर

(some-fn val1 val2 val3) 

तो, जैसे "को हटाने है अनुक्रम के चारों ओर "कोष्ठक"।

3

विषय पर देर से बिट लेकिन मैंने इस उदाहरण को पढ़ने के बाद एक सरल प्रयोग किया। यहां मेरी प्रतिलिपि का नतीजा है, मैं प्रतिक्रिया से कुछ भी कम नहीं कर सकता, लेकिन लगता है कि कम करने और लागू करने के बीच कुछ प्रकार के कैशिंग किक है।

user=> (time (reduce + (range 1e3))) 
"Elapsed time: 5.543 msecs" 
499500 
user=> (time (apply + (range 1e3))) 
"Elapsed time: 5.263 msecs" 
499500 
user=> (time (apply + (range 1e4))) 
"Elapsed time: 19.721 msecs" 
49995000 
user=> (time (reduce + (range 1e4))) 
"Elapsed time: 1.409 msecs" 
49995000 
user=> (time (reduce + (range 1e5))) 
"Elapsed time: 17.524 msecs" 
4999950000 
user=> (time (apply + (range 1e5))) 
"Elapsed time: 11.548 msecs" 
4999950000 

clojure के स्रोत कोड को देखते हुए के साथ अपने सुंदर स्वच्छ प्रत्यावर्तन को कम आंतरिक को कम करने, हालांकि लागू के कार्यान्वयन पर कुछ भी नहीं मिला था। के + Clojure कार्यान्वयन के लिए आंतरिक रूप से लागू होते हैं को कम करने, जो repl है, जो 4 कॉल की व्याख्या करने लगते द्वारा कैश किया गया है आह्वान। क्या कोई इस बात को स्पष्ट कर सकता है कि वास्तव में क्या हो रहा है?

+0

मुझे पता है कि जब भी मैं कर सकता हूं मैं कम करना पसंद करूंगा :) – rohit

+1

आपको 'समय' फ़ॉर्म के अंदर 'रेंज' कॉल नहीं डालना चाहिए। अनुक्रम निर्माण के हस्तक्षेप को हटाने के लिए इसे बाहर रखें। मेरे मामले में, 'कम करें' लगातार 'लागू' से बेहतर प्रदर्शन करता है। – Davyzhu