2012-04-09 17 views
7

मैं lambdas है कि कुछ भारी गणना करने के लिए एक कैश की तरह के रूप में कार्य की एक सूची है चाहता था और इस पर ध्यान:lambdas अंदर सूची comprehensions

>>> [j() for j in [lambda:i for i in range(10)]] 
[9, 9, 9, 9, 9, 9, 9, 9, 9, 9] 

हालांकि

>>> list([lambda:i for i in range(10)]) 
[<function <lambda> at 0xb6f9d1ec>, <function <lambda> at 0xb6f9d22c>, <function <lambda> at 0xb6f9d26c>, <function <lambda> at 0xb6f9d2ac>, <function <lambda> at 0xb6f9d2ec>, <function <lambda> at 0xb6f9d32c>, <function <lambda> at 0xb6f9d36c>, <function <lambda> at 0xb6f9d3ac>, <function <lambda> at 0xb6f9d3ec>, <function <lambda> at 0xb6f9d42c>] 

मतलब है कि lambdas हैं अद्वितीय कार्य लेकिन वे किसी भी तरह से एक ही सूचकांक मूल्य साझा करते हैं।

क्या यह एक बग या सुविधा है? मैं इस समस्या से कैसे बचूं? यह सूची comprehensions तक ही सीमित नहीं है ...

>>> funcs = [] 
... for i in range(10): 
...  funcs.append(lambda:i) 
... [j() for j in funcs] 
[9, 9, 9, 9, 9, 9, 9, 9, 9, 9] 

उत्तर

13

lambda जब आप इसे कॉल करते हैं तो i का मान देता है। चूंकि लूप समाप्त होने के बाद आप lambda पर कॉल करते हैं, तो i का मान हमेशा 9 होगा।

def create_lambda(i): 
    return lambda:i 
>>> [j() for j in [create_lambda(i) for i in range(10)]] 
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9] 
:

>>> [j() for j in [lambda i=i:i for i in range(10)]] 
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9] 

एक अन्य समाधान एक समारोह है कि lambda रिटर्न बनाने के लिए है:

आप समय lambda परिभाषित किया गया था पर मूल्य धारण करने के लिए लैम्ब्डा में एक स्थानीय i चर बना सकते हैं

यह काम करता है क्योंकि create_lambda के प्रत्येक आमंत्रण के लिए बनाया गया एक अलग बंद है (i का एक अलग मूल्य धारण करना)।

+0

पहला दृष्टिकोण तब तक अच्छा काम नहीं करता जब आप अपना 'लैम्ब्डा' '* args' का समर्थन करना चाहते हैं। दूसरा दृष्टिकोण वहां काम करेगा, लेकिन ... और अधिक काम है :) –

+2

बहुत बुरा मैं दोनों उत्तरों के लिए हरा निशान नहीं दे सकता। मैंने इसे चुना क्योंकि यह वास्तव में पांच साल की तरह कट और पेस्ट करने के लिए सही कोड देता है और मुझे एसओ पर जवाब देने के लिए यह दृष्टिकोण पसंद है। – ubershmekel

+0

हमारे लिए पाइथन में कम से कम अच्छे लोगों के लिए, किसी भी मौके पर आप पहले दृष्टिकोण में किसी एक उदाहरण के लिए 'i' से भिन्न चर का उपयोग कर सकते हैं? – Chowlett

0

मुझे यकीन है कि अगर यह एक बग या एक सुविधा है नहीं कर रहा हूँ, लेकिन क्या हो रहा है कि lambda:i लैम्ब्डा समारोह के गठन से पहले मैं का मूल्यांकन नहीं करता है। तो, यह सचमुच सिर्फ एक समारोह है जो मूल्यांकन करता है कि जो भी मेरा वर्तमान मूल्य है। यह कैसे होता है इसका एक और उदाहरण यहां दिया गया है।

>>> i=5 
>>> x=lambda:i 
>>> x() 
5 
>>> i=6 
>>> x() 
6 

तो, जाहिर है, क्या हो रहा है कि मैं एक ही बात को छोड़कर अपने उदाहरण में 9 करने जा रहा है के रूप में यह है कि आदेश में 9 के माध्यम से सीमा 0 के माध्यम से सौंपा जा रहा है है।

मुझे नहीं लगता कि इससे बचने के लिए वास्तव में कोई अच्छा तरीका है। पाइथन में लैम्ब्डा फ़ंक्शन बहुत सीमित हैं। यह वास्तव में दिल में एक कार्यात्मक भाषा नहीं है।

7

जो आप यहां देख रहे हैं वह closures का प्रभाव है। लैम्ब्डा कार्यक्रम से राज्य को बाद में इस्तेमाल करने के लिए कैप्चर कर रहा है। इसलिए जब प्रत्येक लैम्ब्डा एक अद्वितीय वस्तु है, तो राज्य जरूरी नहीं है।

वास्तविक 'गॉन्च्य' यहां, यह है कि परिवर्तनीय i कैप्चर किया गया है, उस समय i उस बिंदु पर प्रतिनिधित्व नहीं करता है। हम एक बहुत आसान उदाहरण के साथ इस उदाहरण देकर स्पष्ट कर सकते हैं:

>>> y = 3 
>>> f = lambda: y 
>>> f() 
3 
>>> y = 4 
>>> f() 
4 

लैम्ब्डा वेरिएबल का संदर्भ के लिए पर रखती है, और मूल्यांकन करता है कि चर जब आप लैम्ब्डा निष्पादित।

इस हल करने के लिए, आप लैम्ब्डा के भीतर एक स्थानीय चर के लिए असाइन कर सकते हैं:

>>> f = lambda y=y:y 
>>> f() 
4 
>>> y = 6 
>>> f() 
4 

अंत में, एक पाश के मामले में, पाश चर केवल है 'घोषित' एक बार। इसलिए, लूप के भीतर लूप चर के किसी भी संदर्भ अगले पुनरावृत्ति से पहले बने रहेंगे। इसमें सूची समझ में परिवर्तनीय शामिल है।

+0

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

+4

@ किथ इरविन, अधिकांश भाषाएं जो बंद करने की अनुमति देती हैं, इस सटीक 'गॉन्च्य' से पीड़ित हैं। जावास्क्रिप्ट और सी # दो उल्लेखनीय उदाहरण हैं। इसके अलावा, ओपी ने शब्द बंद करने का कभी भी उल्लेख नहीं किया, इसलिए मुझे लगा कि मैं उस सामग्री से लिंक करूंगा जो बताता है कि अगर वे अनजान थे। –

+0

यह केवल उन भाषाओं में दिखाई देता है जो मूल्यों और संदर्भों के बीच स्पष्ट रूप से अंतर नहीं करते हैं। उदाहरण के लिए, यह कार्यात्मक भाषाओं के लिस्प परिवार में दिखाई देता है, लेकिन एमएल परिवार या हास्केल या स्कैला जैसी अधिक दृढ़ता से टाइप की गई भाषाओं में नहीं। अगर वह एफ #, ओकम्ल, हास्केल, या स्कैला जैसी भाषा में काम कर रहा था, तो वह उचित विपरीत व्यवहार की अपेक्षा करता है। मुझे लगता है कि उसके पास कार्यात्मक भाषाओं के साथ कम से कम कुछ अनुभव है क्योंकि उन्होंने पोस्ट को कार्यात्मक प्रोग्रामिंग के रूप में टैग किया है। –

1

समस्या यह है कि आप सूची समझ के प्रत्येक पुनरावृत्ति पर मेरे मूल्य को कैप्चर नहीं कर रहे हैं, आप प्रत्येक बार चर को कैप्चर कर रहे हैं।

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

0

मुझे यकीन नहीं है कि आप अपने लैम्ब्डा फ़ंक्शन के साथ क्या करने का प्रयास कर रहे हैं। यह तर्क नहीं लेता है ...

मुझे लगता है कि पायथन (i for i in range(10)) के समान जनरेटर बना रहा है और फिर इसे फिर से चालू कर रहा है। इसके बाद 10 बार गिना जाता है, i का अंतिम मान 9 है, और फिर लैम्ब्डा फ़ंक्शन लौटाता है।

क्या आप किसी प्रकार की वस्तु बनाना चाहते हैं जो संख्याओं को [0 9] से उत्पन्न करेगी? क्योंकि यदि आप ऐसा करना चाहते हैं, तो बस xrange(10) का उपयोग करें, जो एक इटेटरेटर देता है जो संख्याओं को उत्पन्न करता है [0,9]।

def f(x): 
    return x 

lst = [f(x) for x in xrange(10)] 
print(lst == range(10)) # prints True 

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

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