2011-02-04 3 views
10

article written by Daniel Korzekwa में उन्होंने कहा कि निम्नलिखित कोड का प्रदर्शन:स्काला प्रदर्शन सवाल

list.map(e => e*2).filter(e => e>10) 

जावा का उपयोग करके लिखा पुनरावृत्ति समाधान की तुलना में बहुत खराब है।

क्या कोई बता सकता है क्यों? और स्कैला में ऐसे कोड के लिए सबसे अच्छा समाधान क्या है (मुझे आशा है कि यह जावा पुनरावृत्ति संस्करण नहीं है जो स्कैला-फीड है)?

उत्तर

15

कारण कि विशेष कोड धीमी है:

यह तेजी से होना चाहिए। (यदि List और उसके पूर्वजों को विशेषीकृत किया गया तो यह बेहतर किया जा सकता है।) यह शायद 5 या उससे अधिक के कारक से चीजों को धीमा कर देगा।

इसके अलावा, एल्गोरिदम रूप से, उन कार्यों कुछ महंगे हैं क्योंकि आप एक पूरी सूची बनाने, और फिर एक पूरी नई सूची मध्यवर्ती सूची के कुछ घटकों बर्बाद कर रहे हैं। यदि आपने इसे एक झुकाव में किया है, तो आप बेहतर हो जाएंगे। आप की तरह कुछ कर सकता है:

list collect (case e if (e*2>10) => e*2) 

लेकिन क्या हुआ अगर गणना e*2 वास्तव में महंगा है? फिर आप

(List[Int]() /: list)((ls,e) => { val x = e*2; if (x>10) x :: ls else ls } 

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

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

+0

यह उत्तर समस्या पाती है, लेकिन कोई अच्छा समाधान नहीं प्रदान करता है। – Raphael

+1

@ राफेल - लाइब्रेरी की वर्तमान स्थिति को वास्तव में नहीं दिया गया है। जब आप primitives के साथ काम कर रहे हों तो 'view'/'force' आपको सहेजने वाला नहीं है। –

+0

यदि 'ई * 2 महंगा था, तो मध्यवर्ती चरण होने की लागत कम हो जाएगी। शायद स्मृति की समस्याएं, यदि आप बड़ी मात्रा में डेटा से निपटते हैं। – ziggystar

13

मूल रूप से यह एक सूची में दो बार traversing है। एक बार प्रत्येक तत्व को दो के साथ गुणा करने के लिए। और फिर परिणामों को फ़िल्टर करने के लिए एक और बार। एक के रूप में की

Think जबकि पाश मध्यवर्ती परिणामों के साथ एक LinkedList का निर्माण किया। और फिर अंतिम परिणाम देने के लिए फ़िल्टर को लागू करने वाला एक और लूप। क्योंकि यह पुरातन पर काम कर रहा है, लेकिन यह सामान्य संचालन उपयोग कर रहा है, तो पुरातन बॉक्सिंग होने की जरूरत है

list.view.map(e => e * 2).filter(e => e > 10).force 
+0

यह उत्तर बिंदु को याद करता है, हालांकि समाधान सही होता है। – Raphael

+3

मैं अब चालाक हो सकता हूं और इंगित करता हूं कि नमूना कोड में कहीं भी यह कहता है कि इसमें इंट्स शामिल हैं। लेकिन मुझे यह मानना ​​है कि जवाब बेहतर होगा अगर यह उल्लेख किया गया कि मुक्केबाजी और अनबॉक्सिंग चल रहा है। –

2

रेक्स केर सही ढंग से प्रमुख समस्या बताता है: अपरिवर्तनीय सूचियों पर परिचालन, कोड का उल्लिखित टुकड़ा स्मृति में मध्यवर्ती सूचियां बनाता है। ध्यान दें कि यह बराबर जावा कोड की तुलना में जरूरी नहीं है; आप जावा में अपरिवर्तनीय डेटास्ट्रक्चर का कभी भी उपयोग नहीं करते हैं।

विल्फ्रेड स्प्रिंगर एक अच्छा, स्काला idomatic समाधान है। view का उपयोग करके, पूरी सूची की कोई (छेड़छाड़ नहीं) प्रतियां बनाई गई हैं।

ध्यान दें कि दृश्य का उपयोग हमेशा आदर्श नहीं हो सकता है। उदाहरण के लिए, अगर आपकी पहली कॉल filter उस सूची के सबसे फेंक करने की उम्मीद है है, स्पष्ट रूप से छोटा संस्करण बना सकते हैं और बाद में पुनरावृत्तियों के लिए स्मृति इलाके में सुधार के लिए केवल उसके बाद view उपयोग करने के लिए सार्थक हो सकता है।

+0

यह एक उत्तर नहीं है। यह दो अन्य वास्तविक उत्तरों के बारे में एक टिप्पणी है। –

+0

ओह, और हो सकता है * आप * जावा में अपरिवर्तनीय डेटास्ट्रक्चर का कभी भी उपयोग न करें। उपयुक्त होने पर मैं वास्तव में उनका बहुत उपयोग करता हूं। –

+1

1) मैंने दोनों संदर्भित उत्तरों को व्यक्तिगत रूप से कम पाया, इसलिए मैंने एक संयुक्त उत्तर पोस्ट करने का निर्णय लिया। तथ्य यह है कि मैं उचित संदर्भ देता हूं, यह कोई टिप्पणी नहीं करता है। 2) मानक [जावा संग्रह ढांचे] (http://download.oracle.com/javase/6/docs/technotes/guides/collections/reference.html) अपरिवर्तनीय कार्यान्वयन पर काफी कम प्रतीत होता है, जो केवल एक अपरिवर्तनीय प्रदान करता है (! = अपरिवर्तनीय) रैपर। शायद इसीलिए। – Raphael

4

स्कैला दृष्टिकोण अधिक सार और सामान्य है।इसलिए हर एक मामले को अनुकूलित करना मुश्किल है।

मैं कल्पना कर सकता हूं कि हॉटस्पॉट जेआईटी कंपाइलर भविष्य में कोड के लिए स्ट्रीम- और लूप-फ़्यूज़न लागू कर सकता है यदि यह देखता है कि तत्काल परिणाम उपयोग नहीं किए जाते हैं।

इसके अतिरिक्त जावा कोड बस बहुत कुछ करता है।

यदि आप वास्तव में केवल डेटास्ट्रक्चर पर उत्परिवर्तन करना चाहते हैं, तो transform पर विचार करें। यह थोड़ा सा map जैसा दिखता है लेकिन एक नया संग्रह नहीं बनाता है, ई। ग्राम .:

val array = Array(1,2,3,4,5,6,7,8,9,10).transform(_ * 2) 
// array is now WrappedArray(2, 4, 6, 8, 10, 12, 14, 16, 18, 20) 

मैं सच में आशा है कि कुछ अतिरिक्त यथा-स्थान संचालन भविष्य आयन जोड़ दिया जाएगा ...

3

सूची में दो बार traversing बचने के लिए, मुझे लगता है कि for सिंटैक्स का एक अच्छा विकल्प है:

val list2 = for(v <- list1; e = v * 2; if e > 10) yield e 
6

समाधान ज्यादातर जेवीएम के साथ है। हालांकि स्कैला के पास @specialization के आंकड़े में एक कामकाज है, जो किसी भी विशेष वर्ग के आकार को बड़े पैमाने पर बढ़ाता है, और केवल आधा समस्या हल करता है - दूसरा आधा अस्थायी वस्तुओं का निर्माण होता है।

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

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

1

list.filter (ई => ई * 2> 10) .map (ई => ई * 2)

यह प्रयास पहली सूची कम करता है। तो दूसरा ट्रैवर्सिंग कम तत्वों पर है।

 संबंधित मुद्दे

  • कोई संबंधित समस्या नहीं^_^