2008-10-26 3 views
34

में सूचियों का पैटर्न मिलान मैं पाइथन में सूचियों पर कुछ पैटर्न मिलान करना चाहता हूं। उदाहरण के लिए, हास्केल में, मैं कुछ निम्नलिखित की तरह कर सकते हैं:पायथन

fun (head : rest) = ... 

तो जब मैं एक सूची में गुजरती हैं, head पहला तत्व हो जाएगा, और rest अनुगामी तत्वों हो जाएगा।

इसी तरह, पायथन में, मैं अपने आप tuples खोल कर सकते हैं:

(var1, var2) = func_that_returns_a_tuple() 

मैं अजगर में सूचियों के साथ कुछ ऐसा ही करना चाहते हैं। अभी, मैं एक समारोह है कि एक सूची देता है, और जो निम्न है कोड का एक हिस्सा है:

ls = my_func() 
(head, rest) = (ls[0], ls[1:]) 

मैं अगर मैं कर किसी भी तरह हो सकता है सोचा कि अजगर में एक पंक्ति, दो की बजाय में।

उत्तर

55

अब तक मैं जानता हूँ कि वहाँ के रूप में यह एक और क्रिया पेश करने के बिना वर्तमान अजगर में एक एक लाइनर बनाने के लिए कोई रास्ता नहीं है, उदाहरण के लिए है: variadic तर्क हस्ताक्षर के लिए इस्तेमाल किया

split_list = lambda lst: (lst[0], lst[1:]) 
head, rest = split_list(my_func()) 

हालांकि, अजगर 3.0 विशेष वाक्य रचना में और तर्क unpacking रूप में अच्छी तरह खोल सामान्य अनुक्रम के इस प्रकार के लिए उपलब्ध हो जाएगा, तो 3.0 में आप लिखने के लिए सक्षम हो जाएगा:

head, *rest = my_func() 

जानकारी के लिए PEP 3132 देखें।

+0

आप सब कुछ के साथ एक ही लाइन पर कि लैम्ब्डा रख सकते हैं: सिर, बाकी = (लैम्ब्डा एलएसटी: (एलएसटी [0], एलएसटी [1:])) (my_func()) –

+11

हां, लेकिन यह obfuscation पर शुरू करने के लिए शुरू होता है। पाइथन 3 नई सुविधा और पीईपी से जोड़ने के लिए –

+1

+1। – fossilet

4

यह एक बहुत ही शुद्ध 'शुद्ध कार्यात्मक' दृष्टिकोण है और जैसा कि हास्केल में एक समझदार मुहावरे है लेकिन शायद यह पाइथन के लिए उपयुक्त नहीं है। पाइथन में केवल patterns की एक बहुत ही सीमित अवधारणा है - और मुझे संदेह है कि इस तरह के निर्माण को लागू करने के लिए आपको कुछ और कठोर प्रकार की प्रणाली की आवश्यकता हो सकती है (erlang यहां असहमत होने के लिए आमंत्रित buffs)।

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

जैसा कि statedon a few occasionsbefore है, पायथन वास्तव में एक कार्यात्मक भाषा नहीं है। यह सिर्फ एफपी दुनिया से विचार उधार लेता है। यह स्वाभाविक रूप से Tail Recursive नहीं है जिस तरह से आप एक कार्यात्मक भाषा के आर्किटेक्चर में एम्बेडेड देखने की उम्मीद करेंगे, इसलिए आपको बहुत सारे स्टैक स्पेस के बिना बड़े डेटा सेट पर इस तरह के रिकर्सिव ऑपरेशन करने में कुछ कठिनाई होगी।

2

ठीक है, आप इसे पहली जगह में 1-लाइन में क्यों चाहते हैं?

तुम सच में, आप हमेशा इस तरह की एक चाल कर सकते हैं करना चाहते हैं:

def x(func): 
    y = func() 
    return y[0], y[1:] 

# then, instead of calling my_func() call x(my_func) 
(head, rest) = x(my_func) # that's one line :) 
+2

अधिकतर क्योंकि - मानते हैं कि "अच्छा" वाक्यविन्यास है - इसे पढ़ना आसान होगा। – mipadi

+0

इसके अलावा क्योंकि 'सिर' और 'पूंछ' मानक सूचियां हैं। वे किसी कारण के लिए सभी डेटा संरचनाओं और एल्गोरिदम पुस्तक में उपयोग किए जाते हैं। पाइथन में अंतर्निर्मित 'हेड (my_list)' फ़ंक्शन नहीं है, वास्तविक समस्या है। –

32

सबसे पहले पेश किया गया था, कृपया ध्यान दें कि "पैटर्न मिलान" कार्यात्मक भाषाओं और आपके द्वारा उल्लेख किए गए tuples के असाइनमेंट वास्तव में समान नहीं हैं। कार्यात्मक भाषाओं में पैटर्न का उपयोग फ़ंक्शन की आंशिक परिभाषा देने के लिए किया जाता है।तो f (x : s) = e सिर और f के तर्क की पूंछ लेते हैं और उन्हें का उपयोग कर e लौटने का अर्थ यह नहीं है, लेकिन इसका मतलब है कि अगर f का तर्क प्रपत्र x : s (कुछ x और s के लिए) का है, तोf (x : s) है e के बराबर।

पायथन का असाइनमेंट एक से अधिक असाइनमेंट की तरह है (मुझे संदेह है कि इसका मूल इरादा था)। इसलिए, उदाहरण के लिए, x, y = y, xx और y में अस्थायी चर की आवश्यकता के बिना मूल्यों को स्वैप करने के लिए लिखें (जैसा कि आप एक साधारण असाइनमेंट कथन के साथ करेंगे)। पैटर्न पैटर्न के साथ इसका बहुत कम संबंध नहीं है क्योंकि यह मूल रूप से x = y और y = x के "एक साथ" निष्पादन के लिए एक शॉर्टेंड है। हालांकि पाइथन अल्पविराम से अलग सूचियों के बजाय मनमाने ढंग से अनुक्रमों की अनुमति देता है, लेकिन मैं इस पैटर्न मिलान को कॉल करने का सुझाव नहीं दूंगा। पैटर्न मिलान के साथ आप जांचते हैं कि कुछ पैटर्न से मेल खाता है या नहीं; पायथन असाइनमेंट में आपको यह सुनिश्चित करना चाहिए कि दोनों तरफ के अनुक्रम समान हैं।

क्या आप आप आमतौर पर (यह भी कार्यात्मक भाषाओं में) let या where निर्माणों (जो आप गुमनाम कार्यों का उपयोग कर के रूप में मानते सकता है) करने के लिए या तो एक सहायक समारोह (के रूप में दूसरों के द्वारा उल्लेख किया) या कुछ इसी तरह का प्रयोग करेंगे चाहते करने लगते हैं ऐसा करने के लिए।

(head, tail) = (x[0], x[1:]) where x = my_func() 

या, वास्तविक अजगर में: उदाहरण के लिए:

(head, tail) = (lambda x: (x[0], x[1:]))(my_func()) 

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

(क्षमा करें यदि मेरा उत्तर शीर्ष पर एक सा है। मैं तो बस लगता है कि यह अंतर स्पष्ट करना जरूरी है।)

1

वहाँ अजगर रसोई की किताब में एक reciepe यह करने के लिए किया गया था। मैं नहीं कर सकते अब यह पता लगाने के लिए लग रहे हैं, लेकिन यहाँ कोड है (मैं इसे थोड़ा संशोधित)


def peel(iterable,result=tuple): 
    '''Removes the requested items from the iterable and stores the remaining in a tuple 
    >>> x,y,z=peel('test') 
    >>> print repr(x),repr(y),z 
    't' 'e' ('s', 't') 
    ''' 
    def how_many_unpacked(): 
     import inspect,opcode 
     f = inspect.currentframe().f_back.f_back 
     if ord(f.f_code.co_code[f.f_lasti])==opcode.opmap['UNPACK_SEQUENCE']: 
      return ord(f.f_code.co_code[f.f_lasti+1]) 
     raise ValueError("Must be a generator on RHS of a multiple assignment!!") 
    iterator=iter(iterable) 
    hasItems=True 
    amountToUnpack=how_many_unpacked()-1 
    next=None 
    for num in xrange(amountToUnpack): 
     if hasItems:   
      try: 
       next = iterator.next() 
      except StopIteration: 
       next = None 
       hasItems = False 
     yield next 
    if hasItems: 
     yield result(iterator) 
    else: 
     yield None 

हालांकि आपको लगता है कि सिर्फ इसलिए कि जिस तरह से यह पिछले फ्रेम inespects का काम करता है जब एक काम खोल का उपयोग करते हुए नोट करना चाहिए कि ... अभी भी काफी उपयोगी है।

2

अन्य उत्तरों के अलावा, ध्यान दें कि पाइथन में समकक्ष सिर/पूंछ ऑपरेशन, * सिंटैक्स के पायथन 3 के विस्तार सहित आमतौर पर हास्केल के पैटर्न मिलान से कम कुशल होने वाला है।

पायथन सूची वैक्टर के रूप में लागू की जाती है, इसलिए पूंछ प्राप्त करने के लिए सूची की प्रतिलिपि लेनी होगी। यह ओ (एन) सूची का आकार wrt है, जबकि Haskell जैसे लिंक्ड सूचियों का उपयोग कर एक कार्यान्वयन केवल पूंछ सूचक, एक ओ (1) ऑपरेशन का उपयोग कर सकते हैं।

एकमात्र अपवाद इटेटरेटर आधारित दृष्टिकोण हो सकता है, जहां सूची वास्तव में वापस नहीं आती है, लेकिन एक पुनरावर्तक है। हालांकि यह उन सभी स्थानों पर लागू नहीं हो सकता है जहां एक सूची वांछित है (उदाहरण के लिए। कई बार पुनरावृत्त)।

उदाहरण के लिए, Cipher's दृष्टिकोण, अगर इसे ट्यूपल में परिवर्तित करने के बजाय इटरेटर को वापस करने के लिए संशोधित किया गया है तो यह व्यवहार होगा।जाहिर है

def head_tail(lst): 
    it = iter(list) 
    yield it.next() 
    yield it 

>>> a, tail = head_tail([1,2,3,4,5]) 
>>> b, tail = head_tail(tail) 
>>> a,b,tail 
(1, 2, <listiterator object at 0x2b1c810>) 
>>> list(tail) 
[3, 4] 

आप अभी भी बजाय एक उपयोगिता समारोह में रैप करने के लिए इसके लिए अच्छा वाक्यात्मक चीनी किया जा रहा है, हालांकि: वैकल्पिक रूप से एक सरल 2-आइटम एकमात्र तरीका बाईटकोड पर भरोसा नहीं होगा।

2

हास्केल या एमएल के विपरीत, पायथन में अंतर्निहित पैटर्न-संरचनाओं का मिलान नहीं है। पैटर्न मिलान करने का सबसे pythonic तरीका एक कोशिश को छोड़कर ब्लॉक के साथ है:

def recursive_sum(x): 
    try: 
     head, tail = x[0], x[1:] 
     return head + recursive-sum(tail) 
    except IndexError: # empty list: [][0] raises IndexError 
     return 0 

ध्यान दें कि यह केवल टुकड़ा अनुक्रमण के साथ वस्तुओं के साथ काम करता है। इसके अलावा, अगर समारोह जटिल हो जाता है, head, tail लाइन head, tail लाइन इंडेक्स एरर बढ़ा सकता है, जो सूक्ष्म बग का कारण बनता है।

def iterative_sum(x): 
    ret_val = 0 
    for i in x: 
     ret_val += i 
    return ret_val 

यह एक स्पष्ट, सही है: आम तौर पर बेहतर यानी एक संचायक के साथ एक पाश, के रूप में लागू

अजगर में, पूंछ प्रत्यावर्तन है: बहरहाल, यह आप की तरह काम करने के लिए अनुमति नहीं है 99% समय करने का तरीका। न केवल पढ़ने के लिए यह स्पष्ट है, यह तेज़ है और यह सूचियों के अलावा अन्य चीजों पर काम करेगा (उदाहरण के लिए सेट)। यदि वहां कोई अपवाद होने का इंतजार है, तो फ़ंक्शन खुशी से असफल हो जाएगा और इसे श्रृंखला प्रदान करेगा।

2

मैं pyfpm पर काम कर रहा हूं, जो पाइथन में स्केल-जैसी वाक्यविन्यास के साथ मिलान करने वाले पैटर्न के लिए लाइब्रेरी है। आप इस तरह की वस्तुओं को अनपैक करने के लिए इसका इस्तेमाल कर सकते हैं:

from pyfpm import Unpacker 

unpacker = Unpacker() 

unpacker('head :: tail') << (1, 2, 3) 

unpacker.head # 1 
unpacker.tail # (2, 3) 

या एक समारोह के arglist में:

from pyfpm import match_args 

@match_args('head :: tail') 
def f(head, tail): 
    return (head, tail) 

f(1)   # (1,()) 
f(1, 2, 3, 4) # (1, (2, 3, 4)) 
बेशक
+0

बहुत, बहुत अस्पष्ट :) –