Ocaml / F # में फ़ंक्शन डिफ़ॉल्ट रूप से पुनरावर्ती क्यों नहीं हैं?


104

ऐसा क्यों है कि F # और Ocaml (और संभवतः अन्य भाषाओं) में फ़ंक्शन डिफ़ॉल्ट पुनरावर्ती द्वारा नहीं हैं?

दूसरे शब्दों में, भाषा डिजाइनरों ने यह क्यों तय किया यह स्पष्ट रूप से आपको recघोषणा में टाइप करने के लिए एक अच्छा विचार था :

let rec foo ... = ...

और डिफ़ॉल्ट रूप से फ़ंक्शन पुनरावर्ती क्षमता नहीं देते हैं? एक स्पष्ट recनिर्माण की आवश्यकता क्यों है ?


जवाबों:


87

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

भाषाओं के फ्रेंच CAML परिवार में डिफ़ॉल्ट रूप से कार्य पुनरावर्ती नहीं हैं (OCaml सहित)। यह विकल्प letउन भाषाओं का उपयोग करके सुपरसीड फ़ंक्शन (और चर) परिभाषाओं को आसान बनाता है क्योंकि आप एक नई परिभाषा के शरीर के अंदर पिछली परिभाषा को संदर्भित कर सकते हैं। F # को OCaml का यह सिंटैक्स विरासत में मिला है।

उदाहरण के लिए, pOCaml में एक सीक्वेंस के शैनन एन्ट्रापी की गणना करते समय फ़ंक्शन को सुपरसीडिंग करना:

let shannon fold p =
  let p x = p x *. log(p x) /. log 2.0 in
  let p t x = t +. p x in
  -. fold p 0.0

ध्यान दें कि pउच्च-क्रम shannonफ़ंक्शन के तर्क को pशरीर की पहली पंक्ति में दूसरे द्वारा और फिर शरीर pकी दूसरी पंक्ति में कैसे सुपरिचित किया गया है।

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

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

ध्यान दें कि गणेश और एडी द्वारा दिए गए उत्तर लाल झुंड हैं। उन्होंने बताया कि कार्यों के समूहों को एक विशाल के अंदर क्यों नहीं रखा जा सकता let rec ... and ...क्योंकि यह तब प्रभावित होता है जब प्रकार चर सामान्यीकृत हो जाते हैं। इसका recSML में डिफ़ॉल्ट होने से कोई लेना-देना नहीं है लेकिन OCaml नहीं है।


3
मुझे नहीं लगता कि वे लाल झुंड हैं: अगर यह प्रतिबंध पर प्रतिबंध के लिए नहीं था, तो संभावना है कि पूरे कार्यक्रम या मॉड्यूल को स्वचालित रूप से अन्य भाषाओं की तरह पारस्परिक रूप से पुनरावर्ती माना जाएगा। यह विशिष्ट डिजाइन का निर्णय करेगा कि क्या "आरईसी" को जरूरी नहीं होना चाहिए।
जीएस -

"... स्वचालित रूप से अन्य भाषाओं की तरह पारस्परिक रूप से पुनरावर्ती के रूप में व्यवहार किया जाता है"। बेसिक, सी, सी ++, क्लोजर, एर्लांग, एफ #, फैक्टर, फोर्थ, फोरट्रान, ग्रूवी, ओकेमेल, पास्कल, स्मॉलटॉक और स्टैंडर्ड एमएल नहीं।
जद

3
C / C ++ को केवल आगे की परिभाषाओं के लिए प्रोटोटाइप की आवश्यकता होती है, जो कि स्पष्ट रूप से पुनरावृत्ति को चिह्नित करने के बारे में नहीं है। जावा, C # और पर्ल निश्चित रूप से अंतर्निहित पुनरावृत्ति है। हम "सबसे" के अर्थ और प्रत्येक भाषा के महत्व के बारे में एक अंतहीन बहस में पड़ सकते हैं, तो चलिए "बहुत" अन्य भाषाओं के लिए बस जाएं।
जीएस - मोनिका से माफी मांगें

3
"सी / सी ++ को केवल आगे की परिभाषाओं के लिए प्रोटोटाइप की आवश्यकता होती है, जो कि स्पष्ट रूप से पुनरावृत्ति को चिह्नित करने के बारे में नहीं है"। केवल आत्म-पुनरावृत्ति के विशेष मामले में। पारस्परिक पुनरावृत्ति के सामान्य मामले में, सी और सी ++ दोनों में आगे की घोषणाएं अनिवार्य हैं।
जद

2
वास्तव में क्लास स्कोप में C ++ में फॉरवर्ड घोषणाओं की आवश्यकता नहीं होती है, अर्थात बिना किसी घोषणा के एक दूसरे को कॉल करने के लिए स्थिर तरीके ठीक हैं।
polkovnikov.ph

52

के स्पष्ट उपयोग के लिए एक महत्वपूर्ण कारण recहिंडले-मिलनर प्रकार के आविष्कार के साथ करना है, जो सभी स्थैतिक रूप से टाइप किए गए कार्यात्मक प्रोग्रामिंग भाषाओं (विभिन्न तरीकों से परिवर्तित और विस्तारित) को रेखांकित करता है।

यदि आपके पास एक परिभाषा है let f x = x, तो आप इसे टाइप करने 'a -> 'aऔर अलग-अलग पर लागू होने की अपेक्षा करेंगे'a -अलग बिंदुओं प्रकारों पर । लेकिन समान रूप से, यदि आप लिखते हैं, तो आप शेष शरीर के रूप में व्यवहार किए जाने की let g x = (x + 1) + ...अपेक्षा xकरेंगे ।intg

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

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

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


3
त्रुटि, नहीं। आपका पहला पैराग्राफ गलत है (आप "और" और "आरईएस" के स्पष्ट उपयोग के बारे में बात कर रहे हैं) और, परिणामस्वरूप, बाकी अप्रासंगिक है।
JD

5
मैं इस आवश्यकता से प्रसन्न नहीं था। स्पष्टीकरण के लिए धन्यवाद। एक और कारण है कि हास्केल डिजाइन में बेहतर क्यों है।
बेंट रासमुसेन

9
नहीं!!!! यह कैसे हो सकता है?! यह उत्तर स्पष्ट गलत है! कृपया नीचे दिए गए हैरोप का उत्तर पढ़ें या मानक एमएल की परिभाषा (
मिल्नर

9
जैसा कि मैंने अपने जवाब में कहा था कि टाइप इंफ़ेक्शन मुद्दा केवल एक कारण होने के विपरीत, rec की आवश्यकता के कारणों में से एक है। जॉन का जवाब भी एक बहुत ही मान्य उत्तर है (हास्केल के बारे में सामान्य स्नाइड टिप्पणी के अलावा); मुझे नहीं लगता कि दोनों विरोध में हैं।
जीएस -

16
"प्रकार का अनुमान मुद्दा आरई की आवश्यकता के कारणों में से एक है"। तथ्य यह है कि OCaml की आवश्यकता है recलेकिन SML एक स्पष्ट काउंटर उदाहरण नहीं है। यदि आपके द्वारा वर्णित कारणों के लिए टाइप इंट्रेंस समस्या थी, तो OCaml और SML अलग-अलग समाधान नहीं चुन सकते थे, जैसा कि उन्होंने किया था। बेशक, यह है कि आप andहास्केल को प्रासंगिक बनाने के लिए बात कर रहे हैं ।
जद

10

यह एक अच्छा विचार है दो प्रमुख कारण हैं:

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

दूसरे, पुनरावर्ती मूल्य और विशेष रूप से पारस्परिक रूप से पुनरावर्ती मूल्यों के सेट, इसके बारे में तर्क करने के लिए बहुत कठिन हैं, ऐसी परिभाषाएं हैं जो क्रम में आगे बढ़ती हैं, जो पहले से परिभाषित किया गया है उसके शीर्ष पर प्रत्येक नई परिभाषा निर्माण। इस तरह के कोड को पढ़ते समय यह गारंटी है कि पुनरावर्ती के रूप में स्पष्ट रूप से चिह्नित परिभाषाओं को छोड़कर, नई परिभाषाएं केवल पिछली परिभाषाओं को संदर्भित कर सकती हैं।


4

कुछ अनुमान:

  • letन केवल कार्यों को बाँधने के लिए उपयोग किया जाता है, बल्कि अन्य नियमित मूल्यों को भी। मूल्यों के अधिकांश रूपों को पुनरावर्ती होने की अनुमति नहीं है। पुनरावर्ती मूल्यों के कुछ रूपों की अनुमति है (जैसे कार्य, आलसी भाव, आदि), इसलिए इसे इंगित करने के लिए एक स्पष्ट वाक्यविन्यास की आवश्यकता है।
  • गैर-पुनरावर्ती कार्यों को अनुकूलित करना आसान हो सकता है
  • जब आप एक पुनरावर्ती फ़ंक्शन बनाते हैं तो बंद होने वाली प्रविष्टि को फ़ंक्शन में शामिल होने वाली प्रविष्टि को शामिल करने की आवश्यकता होती है (इसलिए फ़ंक्शन पुनरावर्ती रूप से स्वयं को कॉल कर सकता है), जो पुनरावर्ती क्लोज़र को गैर-पुनरावर्ती क्लोज़र की तुलना में अधिक जटिल बनाता है। जब आप पुनरावृत्ति की आवश्यकता नहीं है तो यह सरल गैर पुनरावर्ती बंद करने में सक्षम होने के लिए अच्छा हो सकता है
  • यह आपको पहले से परिभाषित फ़ंक्शन या उसी नाम के मूल्य के संदर्भ में एक फ़ंक्शन को परिभाषित करने की अनुमति देता है; हालांकि मुझे लगता है कि यह बुरा अभ्यास है
  • अतिरिक्त सुरक्षा? यह सुनिश्चित करता है कि आप वही कर रहे हैं जो आपने इरादा किया था। उदाहरण यदि आप इसे पुनरावर्ती होने का इरादा नहीं रखते हैं, लेकिन आपने गलती से फ़ंक्शन के अंदर एक नाम का उपयोग किया है, जो कि फ़ंक्शन के समान नाम के साथ है, तो यह सबसे अधिक शिकायत करेगा (जब तक कि नाम पहले परिभाषित नहीं किया गया है)
  • letनिर्माण के समान है letलिस्प और योजना में निर्माण; जो गैर-पुनरावर्ती हैं। letrecपुनरावर्ती चलो योजना के लिए एक अलग निर्माण है

"मूल्यों के अधिकांश रूपों को पुनरावर्ती होने की अनुमति नहीं है। पुनरावर्ती मूल्यों के कुछ रूपों की अनुमति है (जैसे कार्य, आलसी भाव, आदि), इसलिए इसे इंगित करने के लिए एक स्पष्ट वाक्यविन्यास की आवश्यकता है"। यह F # के लिए सच है, लेकिन मुझे यकीन नहीं है कि यह OCaml के लिए कितना सही है, जहाँ आप कर सकते हैं let rec xs = 0::ys and ys = 1::xs
जेडी

4

अगर यह दिया रहे:

let f x = ... and g y = ...;;

की तुलना करें:

let f a = f (g a)

इसके साथ:

let rec f a = f (g a)

पूर्व fनिर्धारित fको लागू करने के लिए पूर्व निर्धारित को फिर से परिभाषित करता हैg है a। बाद fके लूप को हमेशा के लिए लागू gकरने के लिए फिर से परिभाषित aकरता है, जो आमतौर पर एमएल वेरिएंट में आप नहीं चाहते हैं।

यह कहा, यह एक भाषा डिजाइनर शैली की बात है। बस इसके साथ चलते हैं।


1

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

यह योजना में वर्गीकृत समानता के समान है। (अर्थात eq?, eqv?और equal?)

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