कार्यात्मक प्रोग्रामिंग में "याद" मूल्यों


20

मैंने खुद को कार्यात्मक प्रोग्रामिंग सीखने का काम करने का फैसला किया है। अब तक यह एक विस्फोट रहा है, और मैंने 'प्रकाश को देखा है'। दुर्भाग्य से, मैं वास्तव में किसी भी कार्यात्मक प्रोग्रामर को नहीं जानता हूं जिससे मैं सवालों को उछाल सकता हूं। प्रस्तुत है स्टैक एक्सचेंज।

मैं एक वेब / सॉफ्टवेयर विकास पाठ्यक्रम ले रहा हूं, लेकिन मेरा प्रशिक्षक कार्यात्मक प्रोग्रामिंग से परिचित नहीं है। वह इसका उपयोग करके मेरे साथ ठीक है, और उसने मुझे यह समझने में मदद करने के लिए कहा कि यह कैसे काम करता है ताकि वह मेरे कोड को बेहतर ढंग से पढ़ सके।

मैंने ऐसा करने का सबसे अच्छा तरीका यह तय किया कि एक साधारण गणितीय कार्य को दर्शाते हुए, जैसे किसी मूल्य को एक शक्ति के रूप में बढ़ाया जाए। सिद्धांत रूप में मैं आसानी से एक पूर्वनिर्मित समारोह के साथ कर सकता था, लेकिन यह एक उदाहरण के उद्देश्य को हरा देगा।

वैसे भी, मुझे कुछ कठिनाई हो रही है कि मूल्य कैसे पकड़ें। चूँकि यह कार्यात्मक प्रोग्रामिंग है इसलिए मैं परिवर्तनशील नहीं कर सकता। अगर मैं इसे अनिवार्य रूप से कोड करता, तो यह कुछ इस तरह दिखता:

(निम्नलिखित सभी स्यूडोकोड है)

f(x,y) {
  int z = x;
  for(int i = 0, i < y; i++){
    x = x * z;
  }
  return x;
}

कार्यात्मक प्रोग्रामिंग में, मुझे यकीन नहीं था। मैंने ये ढूंढ निकाला:

f(x,y,z){
  if z == 'null',
    f(x,y,x);
  else if y > 1,
    f(x*z,y-1,z);
  else
    return x;
}

क्या यह सही है? मुझे zदोनों मामलों में एक मूल्य रखने की आवश्यकता है , लेकिन मुझे यकीन नहीं था कि फ़ंक्शन प्रोग्रामिंग में यह कैसे करना है। सिद्धांत रूप में, मैंने जिस तरह से काम किया था, लेकिन मुझे यकीन नहीं था कि यह 'सही' है। इसे करने का कोई बेहतर तरीका है?


32
यदि आप चाहते हैं कि आपके उदाहरण को गंभीरता से लिया जाए, तो क्या यह गणित की समस्या के बजाय व्यावहारिक समस्या को हल करेगा। यह डेवलपर्स के बीच एक क्लिच की तरह है कि "सभी एफपी गणित की समस्याओं को हल करने के लिए अच्छा है," और यदि आपका उदाहरण अभी तक एक और गणितीय कार्य है, तो आप केवल स्टीरियोटाइप को मजबूत कर रहे हैं, बजाय इसके कि आप उपयोगी दिख रहे हैं।
मेसन व्हीलर

12
वास्तविक दुनिया के विचारों को ध्यान में रखते हुए आपका प्रयास वास्तव में बहुत अच्छा है। आपके सभी पुनरावर्ती कॉल पूंछ कॉल हैं , अर्थात, फ़ंक्शन उन्हें कॉल करने के बाद और कुछ नहीं करता है। इसका मतलब है कि एक संकलक या दुभाषिया जो इसका समर्थन करता है, उन्हें अनुकूलित कर सकता है ताकि आपका पुनरावर्ती कार्य एक निश्चित मात्रा के बजाय स्टैक मेमोरी का उपयोग कर सके y
8bittree

1
समर्थन के लिए बहुत बहुत धन्यवाद! मैं अभी भी इस पर बहुत नया हूँ, इसलिए मेरा छद्मकोड सही नहीं है। @MasonWheeler मुझे लगता है, इस मामले में मेरे कोड का वास्तव में गंभीरता से लेने का मतलब नहीं है। मैं अभी भी सीख रहा हूं, और एफपी से प्यार करने का कारण यह है कि यह मैथ-वाई है। मेरे उदाहरण का पूरा बिंदु मेरे शिक्षक को समझाना है कि मैं एफपी का उपयोग क्यों कर रहा हूं। वह वास्तव में यह नहीं समझता कि यह क्या है, इसलिए यह उसे फायदे दिखाने का एक अच्छा तरीका था।
Ucenna

5
आप किस भाषा में कोड लिखने की योजना बनाते हैं? ऐसी शैली का उपयोग करने का प्रयास न करें जो उस भाषा के लिए उपयुक्त नहीं है जिसका आप उपयोग कर रहे हैं।
कार्स्टन एस

2
संभवत: उपयोगी: en.wikipedia.org/wiki/Memoization
एथन बोल्कर

जवाबों:


37

सबसे पहले, "प्रकाश को देखकर" बधाई। आपने अपने क्षितिज का विस्तार करके सॉफ़्टवेयर की दुनिया को एक बेहतर स्थान बनाया है।

दूसरा, ईमानदारी से कोई तरीका नहीं है कि एक प्रोफेसर जो कार्यात्मक प्रोग्रामिंग को नहीं समझता है, वह आपके कोड के बारे में उपयोगी कुछ भी कहने में सक्षम होने जा रहा है, ट्राइट टिप्पणियों जैसे "इंडेंटेशन ऑफ ऑफ" के अलावा। वेब विकास पाठ्यक्रम में यह आश्चर्यजनक नहीं है, क्योंकि HTML / CSS / JavaScript का उपयोग करके अधिकांश वेब विकास किया जाता है। आप वास्तव में वेब विकास सीखने के बारे में कितना ध्यान रखते हैं, इस पर निर्भर करते हुए कि आप अपने प्रोफेसर को पढ़ाने वाले उपकरणों को सीखने के प्रयास में रखना चाहते हैं (दर्दनाक हालांकि यह हो सकता है - मुझे अनुभव से पता है)।

बताए गए प्रश्न को संबोधित करने के लिए: यदि आपका अनिवार्य कोड लूप का उपयोग करता है, तो संभावना है कि आपका कार्यात्मक कोड पुनरावर्ती होने वाला है।

(* raises x to the power of y *)
fun pow (x: real) (y: int) : real = 
    if y = 1 then x else x * (pow x (y-1))

ध्यान दें कि यह एल्गोरिथ्म वास्तव में कमोबेश समान है। वास्तव में, व्यक्ति पुनरावर्ती पुनरावर्ती प्रक्रियाओं के लिए उपरिशायी चीनी को लूप मान सकता है।

एक साइड नोट के रूप zमें, वास्तव में आपके अनिवार्य या कार्यात्मक कोड के मूल्य की कोई आवश्यकता नहीं है। आपको अपने अनिवार्य कार्य को इस तरह लिखना चाहिए:

def pow(x, y):
    var ret = 1
    for (i = 0; i < y; i++)
         ret = ret * x
    return ret

परिवर्तनशील का अर्थ बदलने के बजाय x


आपका पुनरावर्ती powकाफी सही नहीं है। जैसा है, pow 3 3रिटर्न 81, के बजाय 27। यह होना चाहिएelse x * pow x (y-1).
8bittree

3
वूप्स, सही कोड लिखना कठिन है :) फिक्स्ड, और मैंने टाइप एनोटेशन भी जोड़े। @ यूकेना यह एसएमएल माना जाता है, लेकिन मैंने इसका इस्तेमाल थोड़ी देर में नहीं किया है, इसलिए मेरा वाक्य-विन्यास थोड़ा गलत हो सकता है। एक फ़ंक्शन घोषित करने के बहुत सारे रफ़ तरीके हैं, मैं कभी भी सही कीवर्ड को याद नहीं रख सकता। सिंटैक्स परिवर्तनों के अलावा, कोड जावास्क्रिप्ट में समान है।
बाग़ का

2
@jwg जावास्क्रिप्ट में कुछ कार्यात्मक पहलू हैं: कार्य नेस्टेड फ़ंक्शन, रिटर्न फ़ंक्शंस को परिभाषित कर सकते हैं और फ़ंक्शंस को पैरामीटर के रूप में स्वीकार कर सकते हैं; यह लेक्सिकल स्कोप (हालांकि लिस्प डायनेमिक स्कोप नहीं है) के साथ क्लोजर को सपोर्ट करता है। यह प्रोग्रामर के अनुशासन पर निर्भर है कि वह राज्य को बदलने और डेटा को बदलने से परहेज करे।
कैस्पर वैन डेन बर्ग

1
@jwg "कार्यात्मक" भाषा की कोई सहमत-परिभाषा नहीं है (न ही "आवश्यक", "वस्तु-उन्मुख", या "घोषित")। मैं जब भी संभव हो इन शब्दों का उपयोग करने से बचना चाहता हूं। सूर्य के नीचे बहुत सी भाषाएँ हैं जिन्हें चार स्वच्छ समूहों में वर्गीकृत किया गया है।
बाग़ का

1
लोकप्रियता एक भयानक मीट्रिक है, यही वजह है कि जब भी कोई उस भाषा या टूल एक्स का उल्लेख करता है तो बेहतर होना चाहिए क्योंकि यह बहुत व्यापक रूप से उपयोग किया जाता है मुझे पता है कि तर्क को जारी रखना बेकार होगा। मैं व्यक्तिगत रूप से हास्केल की तुलना में भाषाओं के एमएल परिवार से अधिक परिचित हूं। लेकिन मुझे यकीन नहीं है कि यह सच है; मेरा अनुमान है कि डेवलपर्स के विशाल बहुमत ने पहली जगह में हास्केल की कोशिश नहीं की
गार्डेनहैड

33

यह वास्तव में केवल बाग़ के उत्तर के लिए एक परिशिष्ट है, लेकिन मैं यह बताना चाहता हूं कि आपके द्वारा देखे जा रहे पैटर्न के लिए एक नाम है: तह।

कार्यात्मक प्रोग्रामिंग में, एक गुना मूल्यों की एक श्रृंखला को संयोजित करने का एक तरीका है जो प्रत्येक ऑपरेशन के बीच एक मूल्य को "याद" करता है। अनिवार्य रूप से संख्याओं की सूची जोड़ने पर विचार करें:

def sum_all(xs):
  total = 0
  for x in xs:
    total = total + x
  return total

हम मानों की सूची लेने के xsएक और प्रारंभिक अवस्था के 0(के प्रतिनिधित्व वाले totalइस मामले में)। फिर, प्रत्येक xमें xs, हम उस संयोजन को वर्तमान स्थिति के साथ कुछ संयोजन ऑपरेशन (इस मामले में जोड़) के अनुसार जोड़ते हैं , और नए राज्य के रूप में परिणाम का उपयोग करते हैं । संक्षेप में, sum_all([1, 2, 3])के बराबर है (3 + (2 + (1 + 0)))। इस पैटर्न को एक उच्च क्रम फ़ंक्शन में निकाला जा सकता है , एक फ़ंक्शन जो कार्यों को तर्क के रूप में स्वीकार करता है:

def fold(items, initial_state, combiner_func):
  state = initial_state
  for item in items:
    state = combiner_func(item, state)
  return state

def sum_all(xs):
  return fold(xs, 0, lambda x y: x + y)

यह कार्यान्वयन foldअभी भी अत्यावश्यक है, लेकिन इसे पुनरावर्ती रूप से भी किया जा सकता है:

def fold_recursive(items, initial_state, combiner_func):
  if not is_empty(items):
    state = combiner_func(initial_state, first_item(items))
    return fold_recursive(rest_items(items), state, combiner_func)
  else:
    return initial_state

एक गुना के रूप में व्यक्त, आपका कार्य बस है:

def exponent(base, power):
  return fold(repeat(base, power), 1, lambda x y: x * y))

... जहां repeat(x, n)की nप्रतियों की सूची लौटाता है x

कई भाषाएं, विशेष रूप से कार्यात्मक प्रोग्रामिंग की ओर अग्रसर, अपने मानक पुस्तकालय में तह प्रदान करते हैं। यहां तक ​​कि जावास्क्रिप्ट इसे नाम के तहत प्रदान करता है reduce। सामान्य तौर पर, यदि आप किसी प्रकार के लूप में एक मान को "याद" करने के लिए पुनरावृत्ति का उपयोग करते हुए पाते हैं, तो आप संभवतः एक गुना चाहते हैं।


8
निश्चित रूप से स्पॉट करना सीखें जब एक समस्या को एक गुना या एक मानचित्र द्वारा हल किया जा सकता है। एफपी में, लगभग सभी छोरों को गुना या मानचित्र के रूप में व्यक्त किया जा सकता है; इतनी स्पष्ट पुनरावृत्ति आमतौर पर आवश्यक नहीं है।
२०:१६

1
कुछ भाषाओं में, आप सिर्फ लिख सकते हैंfold(repeat(base, power), 1, *)
user253751 23

4
रिको काहलर: scanअनिवार्य रूप से foldजहां मूल्यों की सूची को केवल एक मूल्य में विभाजित करने के बजाय, इसे संयुक्त किया जाता है और प्रत्येक मध्यवर्ती मूल्य को रास्ते से बाहर थूक दिया जाता है, सभी मध्यवर्ती राज्यों की एक सूची का उत्पादन करता है जो सिर्फ उत्पादन के बजाय बनाई गई तह को बताता है। अंतिम स्थिति। यह fold(हर लूपिंग ऑपरेशन है) के संदर्भ में लागू है।
जैक

4
@ रिकोकाहलर और, जहां तक ​​मैं बता सकता हूं, कटौती और सिलवटियां एक ही चीज हैं। हास्केल "फोल्ड" शब्द का उपयोग करता है, जबकि क्लोजर "कम" पसंद करता है। उनका व्यवहार मुझे ऐसा ही लगता है।
Carcigenicate

2
@Ucenna: यह है दोनों एक चर और एक समारोह। कार्यात्मक प्रोग्रामिंग में, फ़ंक्शन केवल संख्याओं और स्ट्रिंग्स की तरह मान हैं - आप उन्हें चर में स्टोर कर सकते हैं, उन्हें अन्य कार्यों के तर्क के रूप में पास कर सकते हैं, उन्हें कार्यों से वापस कर सकते हैं, और आम तौर पर अन्य मूल्यों की तरह उनका इलाज कर सकते हैं। तो combiner_funcएक तर्क है, और sum_allएक अनाम फ़ंक्शन से गुजर रहा है ( lambdaयह थोड़ा है - यह नामकरण के बिना एक फ़ंक्शन मान बनाता है) जो परिभाषित करता है कि यह दो वस्तुओं को एक साथ कैसे संयोजित करना चाहता है।
जैक

8

यह नक्शे और सिलवटों को समझाने में मदद करने के लिए एक पूरक उत्तर है। नीचे दिए गए उदाहरणों के लिए, मैं इस सूची का उपयोग करूंगा। याद रखें, यह सूची अपरिवर्तनीय है, इसलिए यह कभी नहीं बदलेगी:

var numbers = [1, 2, 3, 4, 5]

मैं अपने उदाहरणों में संख्याओं का उपयोग करूंगा क्योंकि वे आसानी से कोड को पढ़ते हैं। हालांकि याद रखें, सिलवटों का इस्तेमाल किसी भी चीज के लिए किया जा सकता है, जिसके लिए पारंपरिक लूप का इस्तेमाल किया जा सकता है।

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

इसका सबसे आसान उदाहरण एक सूची में प्रत्येक संख्या के लिए एक संख्या जोड़ रहा है। भाषा को अज्ञेय बनाने के लिए मैं छद्मकोश का उपयोग करूँगा:

function add-two(n):
    return n + 2

var numbers2 =
    map(add-two, numbers) 

यदि आप प्रिंट करते हैं numbers2, तो आप देखेंगे [3, 4, 5, 6, 7]कि प्रत्येक तत्व में 2 जोड़ के साथ पहली सूची कौन सी है। सूचना का उपयोग add-twoकरने के mapलिए फ़ंक्शन दिया गया था ।

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

function add-together(n1, n2):
    return n1 + n2

var sum =
    fold(add-together, 0, numbers)

यदि आप मुद्रित sumकरते हैं तो आपको संख्याओं की सूची का योग दिखाई देगा: 15।

यहाँ क्या करने के लिए तर्क दिए गए foldहैं:

  1. यह वह फ़ंक्शन है जिसे हम गुना दे रहे हैं। गुना फ़ंक्शन को वर्तमान संचायक, और सूची के वर्तमान आइटम को पारित करेगा। जो भी फ़ंक्शन रिटर्न देता है वह नया संचायक बन जाएगा, जो अगली बार फ़ंक्शन को पास किया जाएगा। जब आप FP- शैली को लूप कर रहे हैं तो यह "वैल्यू" याद है। मैंने इसे एक फ़ंक्शन दिया जो 2 नंबर लेता है और उन्हें जोड़ता है।

  2. यह प्रारंभिक संचयकर्ता है; सूची में किसी भी आइटम को संसाधित करने से पहले संचायक क्या शुरू होता है। जब आप संख्याओं को जोड़ते हैं, तो इससे पहले कि आप किसी संख्या को एक साथ जोड़ते हैं, कुल क्या है? 0, जिसे मैंने दूसरे तर्क के रूप में पारित किया।

  3. अंत में, नक्शे के साथ, हम इसे संसाधित करने के लिए संख्याओं की सूची में भी पास होते हैं।

यदि सिलवटों का अभी भी कोई मतलब नहीं है, तो इस पर विचार करें। जब आप लिखते हैं:

# Notice I passed the plus operator directly this time, 
#  instead of wrapping it in another function. 
fold(+, 0, numbers)

आप मूल रूप से सूची में प्रत्येक आइटम के बीच पारित समारोह डाल रहे हैं, और या तो बाईं या दाईं ओर प्रारंभिक संचयकर्ता जोड़ रहे हैं (इस पर निर्भर करता है कि यह एक बाएं या दाएं गुना है), इसलिए:

[1, 2, 3, 4, 5]

हो जाता है:

0 + 1 + 2 + 3 + 4 + 5
^ Note the initial accumulator being added onto the left (for a left fold).

जो 15 के बराबर है।

एक का उपयोग करें mapजब आप किसी अन्य सूची में एक सूची चालू करने के लिए एक ही लंबाई की, चाहते हैं।

एक का उपयोग करें foldजब आप एक एकल मूल्य में एक सूची चालू करने के लिए, नंबरों की सूची संक्षेप की तरह चाहते हैं।

जैसा कि @ जोर्ग ने टिप्पणियों में बताया, हालांकि, "एकल मूल्य" को एक नंबर की तरह कुछ सरल नहीं होना चाहिए; यह किसी भी एकल वस्तु हो सकती है, जिसमें एक सूची या एक टपल शामिल है! जिस तरह से मेरे पास वास्तव में फोल्ड क्लिक था, वह एक गुना के संदर्भ में एक मानचित्र को परिभाषित करना था । ध्यान दें कि संचायक एक सूची कैसे है:

function map(f, list):
    fold(
        function(xs, x): # xs is the list that has been processed so far
            xs.add( f(x) ) # Add returns the list instead of mutating it
        , [] # Before any of the list has been processed, we have an empty list
        , list) 

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


1
@Ucenna @Ucenna आपके कोड के साथ कुछ दोष है (जैसे iकभी परिभाषित नहीं किया जा रहा है), लेकिन मुझे लगता है कि आपके पास सही विचार है। आपके उदाहरण के साथ एक समस्या है: फ़ंक्शन ( x), एक बार में सूची के केवल एक तत्व को पारित किया जाता है, संपूर्ण सूची को नहीं। पहली बार xकहा जाता है, यह आपका प्रारंभिक संचायक ( y) पारित हो गया है क्योंकि यह पहला तर्क है, और पहला तत्व जैसा कि यह दूसरा तर्क है। अगली बार जब इसे चलाया xजाएगा , तो बाईं ओर नया संचयकर्ता पारित किया जाएगा (जो भी xपहली बार लौटा है), और सूची का दूसरा तत्व जैसा कि यह दूसरा तर्क है।
Carcigenicate

1
@ युकेना अब जब आपके पास मूल विचार है, तो जैक के कार्यान्वयन को फिर से देखें।
Carcigenicate

1
@ युकेन्ना: अलग-अलग लंग्स के लिए अलग-अलग प्राथमिकताएँ होती हैं, चाहे फोल्ड को दिए गए फंक्शन संचयकर्ता को पहले या दूसरे तर्क के रूप में लेते हैं, दुर्भाग्य से। एक कारण यह है कि सिलवटों को सिखाने के लिए एक कम्यूटेटिव ऑपरेशन का उपयोग करना अच्छा है।
जैक

3
" foldजब आप किसी सूची को एकल मान में बदलना चाहते हैं तो उपयोग करें (जैसे संख्याओं की सूची)।" - मैं सिर्फ यह नोट करना चाहता हूं कि यह "एकल मान" एक सूची सहित मनमाने ढंग से जटिल हो सकता है ...! दरअसल, foldपुनरावृति की एक सामान्य विधि है, यह सब कुछ कर सकता है जो पुनरावृत्ति कर सकता है। ईजी mapको तुच्छ रूप से व्यक्त किया जा सकता है func map(f, l) = fold((xs, x) => append(xs, f(x)), [], l)यहां, "एक मूल्य" की गणना foldवास्तव में एक सूची है।
जोर्ग डब्ल्यू मित्तग

2
... संभवतः एक सूची के साथ करना चाहते हैं, के साथ किया जा सकता है fold। और यह एक सूची होने की जरूरत नहीं है, हर संग्रह जो खाली के रूप में व्यक्त किया जा सकता है / खाली नहीं होगा। जिसका मूल रूप से मतलब है कि कोई भी इटेटर करेगा। (मुझे लगता है कि "कैटामोर्फिज़्म" शब्द को फेंकना एक शुरुआतकर्ता के परिचय के लिए बहुत अधिक होगा, हालांकि :
जोर्ग डब्ल्यू मित्तग

1

यह अच्छी समस्याओं को खोजने के लिए मुश्किल है जो कार्यक्षमता में निर्माण के साथ हल नहीं की जा सकती हैं। और अगर यह अंदर बनाया गया है, तो इसे भाषा में अच्छी शैली का उदाहरण माना जाना चाहिए।

उदाहरण के लिए हैसेल में आपके पास पहले से ही (^)Prelude में फ़ंक्शन है।

या यदि आप इसे और अधिक प्रोग्रामेटिक करना चाहते हैं product (replicate y x)

मैं जो कह रहा हूं वह यह है कि यदि आप इसे प्रदान की जाने वाली सुविधाओं का उपयोग नहीं करते हैं, तो एक शैली / भाषा की ताकत दिखाना मुश्किल है। हालाँकि यह यह दिखाने की दिशा में एक अच्छा कदम हो सकता है कि यह पर्दे के पीछे कैसे काम करता है, लेकिन मुझे लगता है कि आपको जिस भी भाषा का उपयोग कर रहे हैं, उसमें सबसे अच्छा तरीका कोड करना चाहिए और फिर वहां के व्यक्ति को यह समझने में मदद करनी चाहिए कि क्या हो रहा है।


1
तार्किक रूप से इस उत्तर को दूसरों से जोड़ने के लिए, यह ध्यान दिया जाना चाहिए कि productयह केवल एक शॉर्टकट फ़ंक्शन है foldजिसके गुणन में इसके कार्य के रूप में और 1 इसके प्रारंभिक तर्क के रूप में है, और यह replicateएक ऐसा फ़ंक्शन है जो इट्रेटर (या सूची; दो से ऊपर अनिवार्य रूप से हैस्केल में अप्रभेद्य हैं) जो एक समान संख्या में आउटपुट देता है। अब यह समझना आसान होना चाहिए कि यह क्रियान्वयन @ जैक के उत्तर के समान ही कैसे करता है, बस उसी कार्यों के पूर्वनिर्धारित विशेष-केस संस्करणों का उपयोग करके इसे और अधिक संक्षिप्त बनाने के लिए।
पेरियाटा ब्रीटा
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.