2012-06-24 11 views
7

एक और एसओ सवाल के लिए एक उत्तर लिखने की कोशिश करते हुए वास्तव में कुछ असाधारण हुआ।पाइथन लैम्बडा मानक कार्यों से अलग तरीके से लागू होते हैं?

मैं मूल रूप से एक एक लाइनर gcd के साथ आया था और कहा it maybe slower because of recursion
gcd = lambda a,b : a if not b else gcd(b, a % b)

यहाँ एक साधारण परीक्षण:

timeit.Timer('gcd(2**2048, 2**2048+123)', setup = 'from fractions import gcd').repeat(3, 100) 
# [0.0022919178009033203, 0.0016410350799560547, 0.0016489028930664062] 
timeit.Timer('gcd(2**2048, 2**2048+123)', setup = 'gcd = lambda a,b : a if not b else gcd(b, a % b)').repeat(3, 100) 
# [0.0020480155944824219, 0.0016460418701171875, 0.0014090538024902344] 

खैर thats दिलचस्प मैं:

assert gcd(10, 3) == 1 and gcd(21, 7) == 7 and gcd(100, 1000) == 100 
यहाँ

कुछ मानक हैं बहुत धीमी होने की उम्मीद है लेकिन समय काफी करीब हैं? शायद मॉड्यूल आयात करना मुद्दा है ...

>>> setup = ''' 
... def gcd(a, b): 
...  """Calculate the Greatest Common Divisor of a and b. 
... 
...  Unless b==0, the result will have the same sign as b (so that when 
...  b is divided by it, the result comes out positive). 
...  """ 
...  while b: 
...   a, b = b, a%b 
...  return a 
... ''' 
>>> timeit.Timer('gcd(2**2048, 2**2048+123)', setup = setup).repeat(3, 100) 
[0.0015637874603271484, 0.0014810562133789062, 0.0014750957489013672] 

नाप अभी भी काफी करीब समय ठीक है, बड़े मूल्यों को आजमाएं।

timeit.Timer('gcd(2**9048, 2**248212)', setup = 'gcd = lambda a,b : a if not b else gcd(b, a % b)').repeat(3, 100) [2.866894006729126, 2.8396279811859131, 2.8353509902954102] 
[2.866894006729126, 2.8396279811859131, 2.8353509902954102] 
timeit.Timer('gcd(2**9048, 2**248212)', setup = setup).repeat(3, 100) 
[2.8533108234405518, 2.8411397933959961, 2.8430981636047363] 

दिलचस्प मुझे आश्चर्य है कि क्या चल रहा है?
मुझे हमेशा लगता है कि एक समारोह को कॉल करने के ऊपरी हिस्से की वजह से रिकर्सन धीमा था, क्या अपवाद है? और मैं अपनी रिकर्सन सीमा तक क्यों नहीं पहुंच पाया?
तो def का उपयोग कर कार्यान्वित मैं इसे सही दूर मारा, अगर मैं 10**9 की तरह कुछ करने के लिए प्रत्यावर्तन गहराई बढ़ाने मैं वास्तव में

>>> setup = ''' 
... import sys 
... sys.setrecursionlimit(10**6) 
... 
... def gcd(a, b): 
...  return a if not b else gcd(b, a % b) 
... ''' 
>>> 
>>> timeit.Timer('gcd(2**9048, 2**248212)', setup = 'gcd = lambda a,b:a if not b else gcd(b, a%b)').repeat(3, 100) 
[3.0647969245910645, 3.0081429481506348, 2.9654929637908936] 
>>> timeit.Timer('gcd(2**9048, 2**248212)', setup = 'from fractions import gcd').repeat(3, 100) 
[3.0753359794616699, 2.97499680519104, 3.0096950531005859] 
>>> timeit.Timer('gcd(2**9048, 2**248212)', setup = setup).repeat(3, 100) 
[3.0334799289703369, 2.9955930709838867, 2.9726388454437256] 
>>> 

भी अधिक एक segmentation fault शायद एक ढेर अतिप्रवाह मिलता है ...

अद्यतन परेशान ...

+0

मुझे लगता है कि यह संभव है कि पाइथन व्याख्याकर्ता आपके लैम्ब्डा अभिव्यक्ति को आपके लिए लूप * में अनुकूलित कर रहा है * (उसी तरह से एक सामान्य लिस्प कार्यान्वयन रिकर्सिव पूंछ कॉल अनुकूलित करेगा)। हालांकि यह सीपीथॉन का कार्यान्वयन विवरण होगा, जो कि सभी पायथन दुभाषियों के लिए जरूरी नहीं है। – Felix

+0

'रिकर्सिव पूंछ कॉल' हाँ, जो मैं भी सोच रहा हूं, फिर भी हम इसे हमेशा लागू कर सकते हैं, इसका मतलब यह नहीं है कि पुनरावृत्ति लैम्बडा का उपयोग करके कुछ बेहतर है तो मानक 'def' वास्तव में परेशान है जब आप मानते हैं कि यह गति या अभिव्यक्ति पर पठनीयता को प्राथमिकता देता है 'http://en.wikipedia.org/wiki/Portal से लिया गया: Python_programming –

+2

@ फ़ेलिक्सबोनकोस्की, पायथन पूंछ रिकर्सन को अनुकूलित नहीं करता है।इस कोड को बस थोड़ी ढेर उपयोग :) – astynax

उत्तर

-1

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

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

रिकर्सन की गति के रूप में - हाँ थोड़ा सा ओवरहेड है, लेकिन स्पष्ट रूप से इतना नहीं है। कॉल ओवरहेड ज्यादातर ढेर फ्रेम आवंटित करने की लागत से बना होगा।

+0

आप भी बार वह दिखा रहा है पर देखने लगे? आप कर रहे हैं ** नहीं ** _effect_ पुस्तकालय कि देखकर function fractions.gcd() तेज है। वह यह दिखाने की कोशिश कर रहा है कि यह टॉस-अप है, और वह उस से परेशान है। -1 प्रश्न पढ़ने के लिए नहीं। – Felix

+1

@ मार्सिन मैंने नियमित पायथन का उपयोग करके गैर-रिकर्स फ़ंक्शन को भी परिभाषित किया है और समय अभी भी अलग नहीं है, कुछ अजीब हो रहा है, और मैं रिकर्सन सीमा क्यों नहीं पहुंची? –

+0

@ samy.vilar आप प्रति स्टैक फ्रेम में केवल एक नया int बना रहे हैं, इसलिए स्मृति खपत कोई मुद्दा नहीं है। यहां कोई रहस्य नहीं है। रिकर्सन ली के रूप में मिट, आप उम्मीद क्यों करेंगे कि आप इसे इस उदाहरण के साथ मार देंगे? – Marcin

6
counter = 0 

def gcd(a, b): 
    global counter 
    counter += 1 
    return a if not b else gcd(b, a % b) 

gcd(2**9048, 2**248212) 
print counter 

प्रिंट 3। बेशक गहराई के रिकर्सन के लिए बहुत अधिक ओवरहेड नहीं है 3.

+0

हां मुझे अब इसके बारे में पता है, मुझे अपने परीक्षण चलाने के लिए फाइबोनैकी संख्याओं का उपयोग करना चाहिए था, रोजाना कुछ नया सीखना चाहिए ... –

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