कार्यात्मक प्रोग्रामिंग से 'फोल्ड' फ़ंक्शन के बराबर 'पायथोनिक' क्या है?


116

हास्केल में निम्नलिखित की तरह कुछ प्राप्त करने का सबसे मुहावरेदार तरीका क्या है:

foldl (+) 0 [1,2,3,4,5]
--> 15

या रूबी में इसके समकक्ष:

[1,2,3,4,5].inject(0) {|m,x| m + x}
#> 15

जाहिर है, पायथन reduceफ़ंक्शन प्रदान करता है , जो गुना का कार्यान्वयन है, बिल्कुल ऊपर के रूप में, हालांकि, मुझे बताया गया था कि प्रोग्रामिंग का 'पायथोनिक' तरीका lambdaशब्दों और उच्च-क्रम के कार्यों से बचने के लिए था , जहां संभव हो सूची-बोध को प्राथमिकता देना। इसलिए, पायथन में एक सूची, या सूची जैसी संरचना को मोड़ने का एक पसंदीदा तरीका है जो reduceफ़ंक्शन नहीं है , या इसे reduceप्राप्त करने का मुहावरेदार तरीका है?


2
sumकाफी अच्छा नहीं है?
जेर्नार्डो

3
यकीन नहीं होता कि यह आपके प्रश्न के लिए एक अच्छा उदाहरण है। यह आसानी से प्राप्त किया जा सकता है sum, आप कुछ अलग प्रकार के उदाहरण प्रदान करना चाह सकते हैं।
जमालिक

14
हे JBernardo - संख्याओं की एक सूची पर संक्षेपण एक बल्कि पतित उदाहरण के रूप में किया गया था, मैं कुछ बाइनरी ऑपरेशन का उपयोग करते हुए एक सूची के तत्वों को जमा करने के सामान्य विचार में अधिक रुचि रखता हूं, और एक शुरुआती मूल्य, विशेष रूप से पूर्णांकों को संक्षेप में नहीं।
मिस्टरटीम

1
@ मिस्टरटिम: sum()वास्तव में इसके साथ सीमित कार्यक्षमता प्रदान करता है। उदाहरण के लिए sum([[a], [b, c, d], [e, f]], [])लौटता [a, b, c, d, e, f]है।
जोएल कॉर्नेट

यद्यपि सूचियों के साथ करने का मामला इस तकनीक के साथ देखने के लिए चीजों का एक अच्छा प्रदर्शन है - +सूचियों में समय और स्मृति दोनों में एक रैखिक समय का संचालन होता है, जिससे संपूर्ण कॉल द्विघात हो जाता है। उपयोग करना list(itertools.chain.from_iterable([a], [b,c,d],[e,f],[]])समग्र रूप से रैखिक है - और यदि आपको केवल एक बार इस पर पुनरावृति करने की आवश्यकता है, तो आप कॉल को listस्मृति के संदर्भ में स्थिर बनाने के लिए छोड़ सकते हैं ।
LVC

जवाबों:


115

किसी सरणी को समेटने का Pythonic तरीका उपयोग कर रहा है sum। अन्य उद्देश्यों के लिए, आप कभी-कभी reduce( functoolsमॉड्यूल से) और मॉड्यूल के कुछ संयोजन का उपयोग कर सकते हैं operator, जैसे:

def product(xs):
    return reduce(operator.mul, xs, 1)

ध्यान रखें कि reduceवास्तव में foldl, हास्केल शब्दों में एक है । सिलवटों को करने के लिए कोई विशेष वाक्यविन्यास नहीं है foldr, कोई बिल्टिन नहीं है , और वास्तव reduceमें गैर-सहयोगी ऑपरेटरों के साथ उपयोग करना खराब शैली माना जाता है।

उच्च-क्रम के कार्यों का उपयोग करना काफी pythonic है; यह पायथन के सिद्धांत का अच्छा उपयोग करता है कि सब कुछ एक वस्तु है, जिसमें फ़ंक्शन और कक्षाएं शामिल हैं। आप सही कह रहे हैं कि लंबोदर को कुछ पाइथोनिस्टस द्वारा भुनाया जाता है, लेकिन ज्यादातर क्योंकि वे जटिल होने पर बहुत पठनीय नहीं होते हैं।


4
@ जेर्नार्डो: आप कह रहे हैं कि बिल्ट मॉड्यूल में कुछ भी पाइथोनिक नहीं है?
फ्रेड फू

4
नहीं, यह कहना बेवकूफी होगी। लेकिन मुझे एक कारण बताइए कि आपको क्यों लगता है कि जीवीआर बिल्डरों से इसे हटाने के बिंदु पर इतना कम कार्य करेगा ?
जेबर्नार्डो

6
@JBernardo: क्योंकि लोग इसके साथ बहुत स्मार्ट ट्रिक्स खेलने की कोशिश करते हैं। उस ब्लॉग पोस्ट से उद्धृत करने के लिए, "की प्रयोज्यता reduce()सहयोगी ऑपरेटरों के लिए बहुत सीमित है, और अन्य सभी मामलों में संचय लूप को स्पष्ट रूप से लिखना बेहतर है।" तो, इसका उपयोग सीमित है, लेकिन यहां तक ​​कि जीवीआर को भी मानक पुस्तकालय में रखने के लिए इसके उपयोगी को पर्याप्त रूप से स्वीकार करना पड़ा।
फ्रेड फू

13
@ जेर्नार्डो, तो क्या इसका मतलब है कि हास्केल और स्कीम में गुना का हर उपयोग समान रूप से बुरा है? यह सिर्फ प्रोग्रामिंग की एक अलग शैली है, इसे अनदेखा करना और अपनी उंगलियों को अपने कानों में डालना और यह स्पष्ट नहीं है कि यह ऐसा नहीं करता है। अधिकांश चीजों की तरह जो एक अलग शैली है, इसके लिए अभ्यस्त होने के लिए अभ्यास करना पड़ता है । यह विचार है कि चीजों को सामान्य श्रेणियों में रखा जाए ताकि कार्यक्रमों के बारे में तर्क करना आसान हो। "ओह, मैं यह करना चाहता हूं, हम्म, एक गुना की तरह दिखता है" (या एक नक्शा, या एक अनकहा, या एक अनकहा तो उस पर एक गुना)
वेस

3
अजगर में लैम्ब्डा में एक से अधिक अभिव्यक्ति नहीं हो सकती हैं। यदि आप कठिन प्रयास करते हैं तो भी आप इसे जटिल नहीं बना सकते। इसलिए पाइथोनिस्टस जो उन्हें पसंद नहीं करते हैं, वे शायद अभी तक उपयोग नहीं किए गए हैं और इसलिए कार्यात्मक प्रोग्रामिंग शैली पसंद नहीं करते हैं।
गोलेम

16

हास्केल

foldl (+) 0 [1,2,3,4,5]

अजगर

reduce(lambda a,b: a+b, [1,2,3,4,5], 0)

जाहिर है, यह एक बिंदु को चित्रित करने के लिए एक तुच्छ उदाहरण है। अजगर में आप बस करेंगे sum([1,2,3,4,5])और यहां तक ​​कि हास्केल शुद्धतावादी आम तौर पर पसंद करेंगे sum [1,2,3,4,5]

गैर-तुच्छ परिदृश्यों के लिए जब कोई स्पष्ट सुविधा कार्य नहीं होता है, तो मुहावरेदार पायथोनिक दृष्टिकोण स्पष्ट रूप से लूप के लिए लिखना है और उपयोग करने के बजाय reduceया परिवर्तनशील परिवर्तनशील असाइनमेंट का उपयोग करना है fold

यह सभी कार्यात्मक शैली में नहीं है, लेकिन यह "पायथोनिक" तरीका है। पायथन को कार्यात्मक शुद्धतावादियों के लिए नहीं बनाया गया है। देखते हैं कि पायथन प्रवाह नियंत्रण के अपवादों को कैसे देखता है कि गैर-कार्यात्मक मुहावरेदार अजगर कैसे होता है।


12
सिलवटों कार्यात्मक "purists" से अधिक के लिए उपयोगी होते हैं। वे सामान्य उद्देश्य सार हैं। कंप्यूटिंग में पुनरावर्ती समस्याएं व्याप्त हैं। सिलवटें बॉयलरप्लेट को हटाने का एक तरीका और पुनरावर्ती समाधानों को भाषाओं में सुरक्षित बनाने का एक तरीका प्रदान करती हैं जो मूल रूप से पुनरावृत्ति का समर्थन नहीं करती हैं। तो बहुत व्यावहारिक बात है। इस क्षेत्र में जीवीआर के पूर्वाग्रह दुर्भाग्यपूर्ण हैं।
इसका शानदार संस्करण

12

पायथन 3 में, reduceहटा दिया गया है: नोट्स जारी करें । फिर भी आप फंक्शनल टूल का उपयोग कर सकते हैं

import operator, functools
def product(xs):
    return functools.reduce(operator.mul, xs, 1)

दूसरी ओर, प्रलेखन forइसके बजाय -लूप की ओर वरीयता व्यक्त करता है reduce, इसलिए:

def product(xs):
    result = 1
    for i in xs:
        result *= i
    return result

8
reduceपायथन 3 मानक पुस्तकालय से हटाया नहीं गया था। मॉड्यूल को दिखाने के reduceलिए ले जाया गया functools
मिट्टी

@ क्लब, मैंने सिर्फ गुइडो के रिलीज़ नोट्स से वाक्यांश लिया है, लेकिन आप सही हो सकते हैं :)
किर

6

शुरू करना Python 3.8, और असाइनमेंट एक्सप्रेशंस (PEP 572) ( :=ऑपरेटर) की शुरूआत , जो एक अभिव्यक्ति के परिणाम को नाम देने की संभावना देता है, हम एक सूची समझ का उपयोग कर सकते हैं, जिसे दोहराने के लिए अन्य भाषाएं फोल्ड / फोल्डेफ़्ट / ऑपरेशंस को कम करती हैं:

एक सूची, एक कम करने वाले कार्य और एक संचायक:

items = [1, 2, 3, 4, 5]
f = lambda acc, x: acc * x
accumulator = 1

हम परिणाम प्राप्त करने के लिए इसके itemsसाथ गुना कर सकते हैं :faccumulation

[accumulator := f(accumulator, x) for x in items]
# accumulator = 120

या संघनित गठन में:

acc = 1; [acc := acc * x for x in [1, 2, 3, 4, 5]]
# acc = 120

ध्यान दें कि यह वास्तव में एक "स्कैनलेफ्ट" ऑपरेशन है क्योंकि सूची की समझ के परिणामस्वरूप प्रत्येक के लिए संचय की स्थिति का प्रतिनिधित्व करता है:

acc = 1
scanned = [acc := acc * x for x in [1, 2, 3, 4, 5]]
# scanned = [1, 2, 6, 24, 120]
# acc = 120

5

आप पहिया को फिर से मजबूत कर सकते हैं:

def fold(f, l, a):
    """
    f: the function to apply
    l: the list to fold
    a: the accumulator, who is also the 'zero' on the first call
    """ 
    return a if(len(l) == 0) else fold(f, l[1:], f(a, l[0]))

print "Sum:", fold(lambda x, y : x+y, [1,2,3,4,5], 0)

print "Any:", fold(lambda x, y : x or y, [False, True, False], False)

print "All:", fold(lambda x, y : x and y, [False, True, False], True)

# Prove that result can be of a different type of the list's elements
print "Count(x==True):", 
print fold(lambda x, y : x+1 if(y) else x, [False, True, True], 0)

आप fअपने पुनरावर्ती मामले में तर्कों को चारों ओर स्वैप करें ।
KayEss

7
क्योंकि पायथन में पूंछ पुनरावृत्ति का अभाव है, यह लंबी सूचियों पर टूट जाएगा और बेकार है। इसके अलावा, इस सही मायने में "तह" समारोह, लेकिन केवल एक छोड़ दिया गुना, यानी foldl, कि है, नहीं है वास्तव में क्या reduceपहले से ही प्रदान करता है (ध्यान दें कि के समारोह हस्ताक्षर को कम है reduce(function, sequence[, initial]) -> value-, यह भी, के लिए एक प्रारंभिक मूल्य दे रही है की कार्यक्षमता भी शामिल है संचायक)।
1693 में cemper93

5

वास्तव में इस सवाल का जवाब नहीं है, लेकिन तह और तह के लिए एक-लाइनर:

a = [8,3,4]

## Foldl
reduce(lambda x,y: x**y, a)
#68719476736

## Foldr
reduce(lambda x,y: y**x, a[::-1])
#14134776518227074636666380005943348126619871175004951664972849610340958208L

2
मुझे लगता है कि यह आपकी तह लिखने का एक बेहतर तरीका है reduce(lambda y, x: x**y, reversed(a)):। अब इसका अधिक प्राकृतिक उपयोग होता है, पुनरावृत्तियों के साथ काम करता है, और कम मेमोरी का उपभोग करता है।
मतीन उल्हाक

2

इस (कम) समस्या का वास्तविक उत्तर है: बस एक लूप का उपयोग करें!

initial_value = 0
for x in the_list:
    initial_value += x #or any function.

यह कम होने की तुलना में अधिक तेज़ होगा और PyPy जैसी चीजें लूप को इस तरह अनुकूलित कर सकती हैं।

BTW, राशि मामले को sumफ़ंक्शन के साथ हल किया जाना चाहिए


5
इसे इस तरह के उदाहरण के लिए पायथोनिक नहीं माना जाएगा।
जमीलक

7
अजगर छोरों कुख्यात धीमी गति से कर रहे हैं। reduceपायथन कार्यक्रम के अनुकूलन का एक सामान्य तरीका है (या गाली देना) ।
फ्रेड फू

2
@ लार्समैन कृपया, यह कहना न आएं कि कम एक साधारण लूप की तुलना में तेज है ... यह हमेशा प्रत्येक फ़ंक्शन के लिए एक फ़ंक्शन कॉल ओवरहेड होगा। इसके अलावा, फिर से, Pypy लूप्स को C स्पीड
JBernardo

1
@ जेरनार्डो: हाँ, मैं यही दावा कर रहा हूँ। मैंने productआपकी शैली में किसी एक के खिलाफ अपने संस्करण को प्रोफाइल किया है, और यह तेज़ (मामूली, हालांकि) है।
फ्रेड फू

1
@ जर्नार्डो ने एक बिलियन फ़ंक्शन (जैसे operator.add) को तर्क के रूप में कम करने के लिए माना: वह अतिरिक्त कॉल एक सी कॉल है (जो पायथन कॉल की तुलना में बहुत सस्ती है), और यह बाईटकोड निर्देशों के एक जोड़े को भेजने और व्याख्या करने से बचाता है, जो आसानी से दर्जनों कॉल का कारण बन सकता है फ़ंक्शन कॉल।

1

मेरा मानना ​​है कि इस प्रश्न के उत्तरदाताओं में से कुछ ने foldसार उपकरण के रूप में फ़ंक्शन के व्यापक निहितार्थ को याद किया है । हां, sumपूर्णांक की सूची के लिए एक ही काम कर सकते हैं, लेकिन यह एक तुच्छ मामला है। foldअधिक सामान्य है। यह उपयोगी है जब आपके पास अलग-अलग आकार के डेटा संरचनाओं का एक क्रम होता है और एक एकत्रीकरण को स्पष्ट रूप से व्यक्त करना चाहते हैं। इसलिए forएक समग्र चर के साथ एक लूप का निर्माण करने और मैन्युअल रूप से इसे हर बार फिर से कनेक्ट करने के बजाय , एक foldफ़ंक्शन (या पाइथन संस्करण, जो इसके reduceअनुरूप दिखाई देता है) प्रोग्रामर को एकत्रीकरण के इरादे को स्पष्ट रूप से प्रदान करने की अनुमति देता है। दो चीज़ें:

  • एकत्रीकरण के लिए एक डिफ़ॉल्ट शुरुआत या "बीज" मूल्य।
  • एक फ़ंक्शन जो एकत्रीकरण के वर्तमान मूल्य ("बीज" के साथ शुरू) और सूची में अगले तत्व को लेता है, और अगले एकत्रीकरण मूल्य को वापस करता है।

हाय rq_! मुझे लगता है कि आपके उत्तर को बेहतर बनाया जाएगा और यदि आप गैर-तुच्छ उदाहरण देते हैं तो बहुत अच्छा सौदा जोड़ सकते हैं fold, जो पायथन में सफाई से करना मुश्किल है, और फिर " fold" कि पायथन में :-)
स्कॉट स्किल्स

0

मुझे पार्टी में आने में काफी देर हो सकती है, लेकिन हम foldrसाधारण लैम्ब्डा कैलकुलस और करी फ़ंक्शन का उपयोग करके कस्टम बना सकते हैं । यहाँ अजगर में तह का मेरा कार्यान्वयन है।

def foldr(func):
    def accumulator(acc):
        def listFunc(l):
            if l:
                x = l[0]
                xs = l[1:]
                return func(x)(foldr(func)(acc)(xs))
            else:
                return acc
        return listFunc
    return accumulator  


def curried_add(x):
    def inner(y):
        return x + y
    return inner

def curried_mult(x):
    def inner(y):
        return x * y
    return inner

print foldr(curried_add)(0)(range(1, 6))
print foldr(curried_mult)(1)(range(1, 6))

भले ही कार्यान्वयन पुनरावर्ती है (धीमा हो सकता है), यह क्रमशः मूल्यों 15और प्रिंट करेगा120

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.