क्लोजर: कम बनाम लागू करें


126

मैं समझता हूँ के बीच वैचारिक अंतर reduceऔर apply:

(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)

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

जवाबों:


125

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

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

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

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


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

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

6
अजीब बात है, मेरी सलाह इसके विपरीत है: reduceजब आप संदेह में होते हैं, applyजब आप जानते हैं कि एक अनुकूलन है। reduceअनुबंध अधिक सटीक है और इस प्रकार सामान्य अनुकूलन की संभावना अधिक है। applyअधिक अस्पष्ट है और इस प्रकार केवल केस-बाय-केस आधार पर अनुकूलित किया जा सकता है। strऔर concatदो प्रचलित अपवाद हैं।
cgrand

1
@cgrand मेरे तर्क का एक rephrasing मोटे तौर पर हो सकता है कि कार्यों जहां के लिए reduceऔर applyपरिणाम के मामले में बराबर हैं, मैं कैसे सबसे अच्छा उनके variadic अधिभार अनुकूलन करने के लिए पता करने के लिए और बस के मामले में इसे लागू सवाल में समारोह के लेखक उम्मीद थी reduceअगर यह वास्तव में वही है जो सबसे अधिक समझ में आता है (ऐसा करने का विकल्प निश्चित रूप से हमेशा उपलब्ध है और एक प्रचलित समझदार डिफ़ॉल्ट के लिए बनाता है)। मैं देखता हूं कि आप कहां से आ रहे हैं, हालांकि, reduceक्लोजर के प्रदर्शन की कहानी के लिए निश्चित रूप से केंद्रीय है (और तेजी से ऐसा), बहुत उच्च अनुकूलित और बहुत स्पष्ट रूप से निर्दिष्ट।
माइकल मार्कीज

51

इस जवाब को देखने वाले नए लोगों के लिए,
सावधान रहें, वे समान नहीं हैं:

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

21

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

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


19

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


9

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

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

(apply + 1 2 other-number-list)

9

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

(reduce + some-numbers)

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

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


7

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

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

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) (vals 2))

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

(apply some-fn vals)

और इसे इसके समकक्ष परिवर्तित किया गया है:

(some-fn val1 val2 val3)

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


4

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

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

क्लोजर के स्रोत कोड को देखते हुए आंतरिक-कम के साथ इसकी सुंदर साफ पुनरावृत्ति को कम करें, हालांकि आवेदन के कार्यान्वयन पर कुछ भी नहीं मिला। आंतरिक रूप से लागू होने को कम करने के लिए + का क्लोजर कार्यान्वयन, जिसे उत्तर द्वारा कैश किया जाता है, जो 4 वीं कॉल को समझाते हैं। क्या कोई स्पष्ट कर सकता है कि वास्तव में यहाँ क्या हो रहा है?


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

2
आपको फॉर्म के rangeअंदर कॉल नहीं करना चाहिए time। अनुक्रम निर्माण के हस्तक्षेप को हटाने के लिए इसे बाहर रखें। मेरे मामले में, reduceलगातार बेहतर प्रदर्शन करते हैं apply
डेविहु

3

आवेदन की सुंदरता को फंक्शन दिया गया है (इस मामले में +) एक लंबित संग्रह के साथ पूर्व-लंबित हस्तक्षेप तर्क द्वारा गठित तर्क सूची में लागू किया जा सकता है। कम करना संग्रह आइटम को प्रत्येक के लिए फ़ंक्शन को लागू करने के लिए एक अमूर्त प्रक्रिया है और चर args मामले के साथ काम नहीं करता है।

(apply + 1 2 3 [3 4])
=> 13
(reduce + 1 2 3 [3 4])
ArityException Wrong number of args (5) passed to: core/reduce  clojure.lang.AFn.throwArity (AFn.java:429)
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.