कार्यात्मक प्रोग्रामिंग में गणितीय कानूनों के माध्यम से कोई कैसे मॉड्यूलरता प्राप्त करता है?


11

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

क्या आप इसे मुझे समझा सकते हैं और एक उदाहरण दे सकते हैं?


7
"यह यूनिट परीक्षण की तुलना में बहुत आसान और तेज़ लगता है"। हाँ, लगता है। वास्तव में, अधिकांश सॉफ्टवेयर के लिए यह व्यावहारिक रूप से असंभव है। और शीर्षक क्यों मॉड्यूलरिटी का उल्लेख कर रहा है फिर भी आपके सत्यापन की बात कर रहे हैं?
व्यंग्यात्मक

@ ईओओफ़ोरिक इन यूनिट टेस्टिंग ओओपी में आप वेरिफिकेशन के लिए टेस्ट लिखते हैं ... वेरिफिकेशन करते हैं कि सॉफ्टवेयर का एक हिस्सा सही तरीके से काम कर रहा है, लेकिन यह भी सत्यापित करें कि आपकी चिंताएँ अलग हैं ... यानी मोड्युलैरिटी और रीससबेलिटी ... अगर मैं इसे सही तरीके से समझता हूं।
leeand00

2
@ यूफोरिक केवल यदि आप उत्परिवर्तन और वंशानुक्रम का दुरुपयोग करते हैं और त्रुटिपूर्ण प्रकार की प्रणालियों (जैसे है null) वाली भाषाओं में काम करते हैं ।
डोभाल

@ leeand00 मुझे लगता है कि आप "सत्यापन" शब्द का दुरुपयोग कर रहे हैं। सॉफ्टवेयर सत्यापन द्वारा मॉड्युलैरिटी और पुन: प्रयोज्य की सीधे जाँच नहीं की जाती है (हालांकि, निश्चित रूप से, मॉड्यूलरिटी की कमी सॉफ्टवेयर को बनाए रखने और पुन: उपयोग करने के लिए कठिन बना सकती है, इसलिए बग्स को प्रस्तुत करना और सत्यापन प्रक्रिया को विफल करना)।
एंड्रेस एफ।

यदि यह मॉड्यूलर तरीके से लिखा गया है, तो सॉफ्टवेयर के कुछ हिस्सों को सत्यापित करना बहुत आसान है। तो आपके पास वास्तविक सबूत हो सकता है कि फ़ंक्शन कुछ कार्यों के लिए सही ढंग से काम करता है, दूसरों के लिए आप यूनिट परीक्षण लिख सकते हैं।
grizwako

जवाबों:


22

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

मान लीजिए कि हम द्विआधारी पेड़ लगा रहे हैं जिसमें पूर्णांक मान शामिल हैं (सिंटैक्स को सरल रखने के लिए, मैं इसमें सामान्य प्रोग्रामिंग नहीं लाऊंगा, हालांकि यह कुछ भी नहीं बदलेगा।) मानक एमएल में, मैं इसे परिभाषित करूंगा। इस:

datatype tree = Empty | Node of (tree * int * tree)

यह एक नए प्रकार का परिचय देता है, treeजिसका मान बिल्कुल दो किस्मों (या कक्षाओं में आ सकता है, एक वर्ग की ओओपी अवधारणा से भ्रमित नहीं होना) - एक Emptyमूल्य जो कोई जानकारी नहीं देता है, और वे Nodeमूल्य जो 3-ट्यूपल ले जाते हैं जिनका पहला और अंतिम तत्व हैं treeऔर जिनका मध्य तत्व ए है int। OOP में इस घोषणा के लिए निकटतम सन्निकटन इस तरह दिखेगा:

public class Tree {
    private Tree() {} // Prevent external subclassing

    public static final class Empty extends Tree {}

    public static final class Node extends Tree {
        public final Tree leftChild;
        public final int value;
        public final Tree rightChild;

        public Node(Tree leftChild, int value, Tree rightChild) {
            this.leftChild = leftChild;
            this.value = value;
            this.rightChild = rightChild;
        }
    }
}

कैवेट के साथ कि प्रकार ट्री के चर कभी नहीं हो सकते null

अब चलो पेड़ की ऊंचाई (या गहराई) की गणना करने के लिए एक फ़ंक्शन लिखते हैं, और मान लेते हैं कि हमारे पास एक ऐसे maxफ़ंक्शन की पहुंच है जो दो साल के बड़े रिटर्न देता है:

fun height(Empty) =
        0
 |  height(Node (leftChild, value, rightChild)) =
        1 + max( height(leftChild), height(rightChild) )

हमने heightफ़ंक्शन को मामलों द्वारा परिभाषित किया है - Emptyपेड़ों के लिए एक परिभाषा है और Nodeपेड़ों के लिए एक परिभाषा है । कंपाइलर जानता है कि पेड़ों के कितने वर्ग मौजूद हैं और यदि आप दोनों मामलों को परिभाषित नहीं करते हैं तो चेतावनी जारी करेंगे। अभिव्यक्ति Node (leftChild, value, rightChild)समारोह हस्ताक्षर में चर के 3-टपल के मूल्यों बांधता है leftChild, valueऔर rightChildक्रमशः तो हम समारोह परिभाषा उन्हें देख सकते हैं। यह एक ओओपी भाषा में इस तरह से स्थानीय चर घोषित करने के समान है:

Tree leftChild = tuple.getFirst();
int value = tuple.getSecond();
Tree rightChild = tuple.getThird();

हम कैसे साबित कर सकते हैं कि हमने heightसही तरीके से लागू किया है? हम स्ट्रक्चरल इंडक्शन का उपयोग कर सकते हैं , जिसमें निम्न शामिल हैं: 1. साबित करें कि heightहमारे treeप्रकार के बेस केस (एस) में सही है ( Empty2) मान लें कि पुनरावर्ती कॉल heightसही हैं, तो साबित करें कि heightगैर-आधार मामले के लिए सही है ) (जब पेड़ वास्तव में है Node)।

चरण 1 के लिए, हम देख सकते हैं कि फ़ंक्शन हमेशा 0 देता है जब तर्क एक Emptyपेड़ है। यह एक पेड़ की ऊंचाई की परिभाषा से सही है।

चरण 2 के लिए, फ़ंक्शन वापस लौटता है 1 + max( height(leftChild), height(rightChild) )। यह मानते हुए कि पुनरावर्ती कॉल वास्तव में बच्चों की ऊंचाई को वापस करते हैं, हम देख सकते हैं कि यह भी सही है।

और वह प्रमाण पूरा करता है। चरण 1 और 2 ने सभी संभावनाओं को मिला दिया। ध्यान दें, हालांकि, हमारे पास कोई उत्परिवर्तन, कोई नल नहीं है, और बिल्कुल दो प्रकार के पेड़ हैं। उन तीन स्थितियों को हटा दें और यदि अव्यवहारिक नहीं है, तो सबूत जल्दी से अधिक जटिल हो जाता है।


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

एक फ़ंक्शन दो स्थितियों में एक मूल्य वापस करने में विफल हो सकता है: यदि यह एक अपवाद फेंकता है, और यदि यह हमेशा के लिए बंद हो जाता है। पहले यह साबित करते हैं कि यदि कोई अपवाद नहीं फेंका जाता है, तो कार्य समाप्त हो जाता है:

  1. साबित करें कि (यदि कोई अपवाद नहीं फेंका जाता है) फ़ंक्शन आधार मामलों के लिए समाप्त हो जाता है ( Empty)। चूंकि हम बिना शर्त 0 वापस करते हैं, इसलिए यह समाप्त हो जाता है।

  2. साबित करें कि फ़ंक्शन गैर-बेस मामलों ( Node) में समाप्त होता है । वहाँ तीन फ़ंक्शन कॉल यहाँ है: +, max, और height। हम यह जानते हैं +और maxक्योंकि वे भाषा के मानक पुस्तकालय की कर रहे हैं हिस्सा समाप्त कर देंगे और वे जिस तरह से है कि परिभाषित कर रहे हैं। जैसा कि पहले उल्लेख किया गया है, हमें यह मानने की अनुमति है कि हम जिस संपत्ति को साबित करने की कोशिश कर रहे हैं, वह पुनरावर्ती कॉलों पर तब तक सही है जब तक वे तत्काल उपप्रकारों पर काम करते हैं, इसलिए कॉल heightभी समाप्त हो जाते हैं।

वह प्रमाण को समाप्त करता है। ध्यान दें कि आप एक इकाई परीक्षण के साथ समाप्ति साबित नहीं कर पाएंगे। अब जो कुछ बचा है वह heightयह दिखाना है कि अपवाद नहीं फेंकता है।

  1. साबित करें कि heightआधार मामले पर अपवाद नहीं फेंकते हैं ( Empty)। रिटर्निंग 0 एक अपवाद नहीं फेंक सकता है, इसलिए हम कर रहे हैं।
  2. साबित करें कि heightगैर-आधार मामले पर अपवाद को फेंकना नहीं है ( Node)। एक बार फिर से मान लें कि हम जानते हैं +और maxअपवाद नहीं फेंकते हैं। और संरचनात्मक प्रेरण हमें अनुमान लगाने की अनुमति देता है कि पुनरावर्ती कॉल या तो नहीं फेंकेंगे (क्योंकि पेड़ के तत्काल बच्चों पर काम करते हैं।) लेकिन रुको! यह कार्य पुनरावर्ती है, लेकिन पुनरावर्ती नहीं है । हम ढेर को उड़ा सकते हैं! हमारे प्रयास के सबूत ने बग को उजागर किया है। हम पूंछ पुनरावर्ती होने के लिए इसे बदलकरheight ठीक कर सकते हैं ।

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

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

8
  1. कोड के बारे में तर्क करना बहुत आसान है जब सब कुछ अपरिवर्तनीय है । नतीजतन, लूप को अक्सर पुनरावृत्ति के रूप में लिखा जाता है। सामान्य तौर पर, पुनरावर्ती समाधान की शुद्धता को सत्यापित करना आसान है। अक्सर, इस तरह के समाधान भी समस्या की गणितीय परिभाषा के समान ही पढ़ेंगे।

    हालांकि, ज्यादातर मामलों में शुद्धता का वास्तविक औपचारिक प्रमाण देने के लिए बहुत कम प्रेरणा है। सबूत मुश्किल हैं, बहुत (मानव) समय लेते हैं, और कम आरओआई होते हैं।

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

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

    मुझे बहुत उदार भाषाएं, और बहुत प्रतिबंधित भाषाएं पसंद हैं, और दोनों में अपनी-अपनी कठिनाइयाँ हैं। लेकिन ऐसा नहीं है कि एक "बेहतर" होगा, प्रत्येक अलग तरह के कार्य के लिए अधिक सुविधाजनक है।

फिर यह बताया जाना चाहिए कि सबूत और इकाई परीक्षण विनिमेय नहीं हैं। वे दोनों हमें कार्यक्रम की शुद्धता पर सीमा लगाने की अनुमति देते हैं:

  • परीक्षण शुद्धता पर एक ऊपरी सीमा रखता है: यदि कोई परीक्षण विफल रहता है, तो कार्यक्रम गलत है, यदि कोई परीक्षण विफल नहीं होता है, तो हम निश्चित हैं कि कार्यक्रम परीक्षण किए गए मामलों को संभाल लेगा, लेकिन अभी भी अनदेखा बग हो सकता है।

    int factorial(int n) {
      if (n <= 1) return 1;
      if (n == 2) return 2;
      if (n == 3) return 6;
      return -1;
    }
    
    assert(factorial(0) == 1);
    assert(factorial(1) == 1);
    assert(factorial(3) == 6);
    // oops, we forgot to test that it handles n > 3…
    
  • सबूत शुद्धता पर एक कम बाध्य डालते हैं: कुछ गुणों को साबित करना असंभव हो सकता है। उदाहरण के लिए, यह साबित करना आसान हो सकता है कि कोई फ़ंक्शन हमेशा एक नंबर लौटाता है (यह है कि सिस्टम क्या करते हैं)। लेकिन यह साबित करना असंभव हो सकता है कि संख्या हमेशा रहेगी < 10

    int factorial(int n) {
      return n;  // FIXME this is just a placeholder to make it compile
    }
    
    // type system says this will be OK…
    

1
"कुछ गुणों को साबित करना असंभव हो सकता है ... लेकिन यह साबित करना असंभव हो सकता है कि संख्या हमेशा <10. होगी" यदि कार्यक्रम की शुद्धता 10 से कम होने की संख्या पर निर्भर करती है, तो आपको इसे साबित करने में सक्षम होना चाहिए। यह सच है कि टाइप सिस्टम कम से कम (वैध कार्यक्रमों के एक टन को सत्तारूढ़ किए बिना नहीं) कर सकता है - लेकिन आप कर सकते हैं।
डोभाल

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

1
सहमत, मुझे लगता है कि उदाहरण थोड़ा भ्रामक था।
डोभाल

2
इदरीश की तरह, भरोसेमंद टाइप की गई भाषाओं में, इसे 10 से कम रिटर्न साबित करना संभव हो सकता है
Ingo

2
शायद चिंता का एक बेहतर तरीका यह है कि @Doval यह बताता है कि कुछ समस्याएं अनिर्दिष्ट हैं (उदाहरण के लिए समस्या को हल करना), साबित करने के लिए बहुत अधिक समय की आवश्यकता होती है, या परिणाम को साबित करने के लिए नए गणित की आवश्यकता होगी। मेरी व्यक्तिगत राय है कि आपको स्पष्ट करना चाहिए कि अगर कुछ सही साबित होता है, तो उसे परीक्षण करने की कोई आवश्यकता नहीं है। प्रमाण पहले से ही एक ऊपरी और निचले हिस्से को रखता है। साक्ष्य और परीक्षण विनिमेय नहीं होने का कारण यह है कि एक प्रमाण करना बहुत कठिन हो सकता है या करना असंभव है। इसके अलावा परीक्षण स्वचालित हो सकते हैं (जब कोड बदलता है)।
थॉमस ईडिंग

7

चेतावनी का एक शब्द यहाँ क्रम में हो सकता है:

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

ऐसा इसलिए है क्योंकि हमारे पास क्विकचेक जैसे उपकरण हैं, जो स्वचालित रूप से और यादृच्छिक रूप से परीक्षण मामलों को उत्पन्न करते हैं। आप केवल उन कानूनों को बताते हैं जिन्हें एक फ़ंक्शन को मानना ​​चाहिए, और फिर सैकड़ों यादृच्छिक परीक्षण मामलों के लिए क्विकचेक इन कानूनों की जांच करेगा।

आप देखते हैं, यह मुट्ठी भर परीक्षण मामलों की तुच्छ समानता जांचों की तुलना में थोड़ा अधिक है।

एवीएल पेड़ के कार्यान्वयन से एक उदाहरण यहां दिया गया है:

--- A generator for arbitrary Trees with integer keys and string values
aTree = arbitrary :: Gen (Tree Int String)


--- After insertion, a lookup with the same key yields the inserted value        
p_insert = forAll aTree (\t -> 
             forAll arbitrary (\k ->
               forAll arbitrary (\v ->
                lookup (insert t k v) k == Just v)))

--- After deletion of a key, lookup results in Nothing
p_delete = forAll aTree (\t ->
            not (null t) ==> forAll (elements (keys t)) (\k ->
                lookup (delete t k) k == Nothing))

दूसरा नियम (या संपत्ति) जिसे हम निम्नानुसार पढ़ सकते हैं: सभी मनमाने पेड़ों के लिए t, निम्नलिखित धारण करते हैं: यदि tखाली नहीं है, तो kउस पेड़ की सभी कुंजियों के लिए यह धारण करेगा कि उस पेड़ kको देखना जो हटाने का परिणाम है kसे t, परिणाम होगा Nothing(जो इंगित करता है: नहीं मिला)।

यह मौजूदा कुंजी को हटाने के लिए उचित कार्यक्षमता की जांच करता है। गैर-मौजूदा कुंजी को हटाने के लिए किन कानूनों को नियंत्रित करना चाहिए? हम निश्चित रूप से परिणामी पेड़ को उसी के समान चाहते हैं जिसे हमने हटा दिया था। हम इसे आसानी से व्यक्त कर सकते हैं:

p_delete_nonexistant = forAll aTree (\t ->
                          forAll arbitrary (\k -> 
                              k `notElem` keys t ==> delete t k == t))

इस तरह, परीक्षण वास्तव में मजेदार है। और इसके अलावा, एक बार जब आप क्विकचेक गुण पढ़ना सीख जाते हैं, तो वे एक मशीन परीक्षण योग्य विनिर्देश के रूप में काम करते हैं ।


4

मुझे ठीक से समझ नहीं आया कि "गणितीय कानूनों के माध्यम से प्रतिरूपता हासिल करने" से जुड़ा उत्तर क्या है, लेकिन मुझे लगता है कि मुझे इस बात का अंदाजा है कि इसका क्या मतलब है।

की जाँच करें functor :

फ़नकार वर्ग को इस तरह परिभाषित किया गया है:

 class Functor f where
   fmap :: (a -> b) -> f a -> f b

यह परीक्षण मामलों के साथ नहीं आता है, बल्कि इसके बजाय, कुछ कानूनों के साथ संतुष्ट होना चाहिए।

फ़नकार के सभी उदाहरणों का पालन करना चाहिए:

 fmap id = id
 fmap (p . q) = (fmap p) . (fmap q)

अब हम कहते हैं कि आप लागू करें Functor( स्रोत ):

instance  Functor Maybe  where
    fmap _ Nothing       = Nothing
    fmap f (Just a)      = Just (f a)

समस्या यह है कि आपका कार्यान्वयन कानूनों को संतुष्ट करता है। आप ऐसा कैसे करते हैं?

एक दृष्टिकोण परीक्षण मामलों को लिखना है। इस दृष्टिकोण की मौलिक सीमा यह है कि आप मामलों की एक सीमित संख्या में व्यवहार की पुष्टि कर रहे हैं (सौभाग्य से 8 मापदंडों के साथ एक समारोह का परीक्षण कर रहे हैं!), और इसलिए उत्तीर्ण परीक्षण कुछ भी गारंटी नहीं दे सकते हैं लेकिन परीक्षण पास करते हैं।

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

मैं आपको एक वास्तविक औपचारिक प्रमाण के माध्यम से मार्गदर्शन नहीं कर सकता कि उपरोक्त Functorउदाहरण कानूनों को संतुष्ट करता है, लेकिन मैं कोशिश करूंगा और इस बात की रूपरेखा दूंगा कि सबूत कैसा दिख सकता है:

  1. fmap id = id
    • अगर हमारे पास है Nothing
      • fmap id Nothing= Nothingकार्यान्वयन के भाग 1 द्वारा
      • id NothingNothingकी परिभाषा से =id
    • अगर हमारे पास है Just x
      • fmap id (Just x)= Just (id x)= Just xकार्यान्वयन के भाग 2 के द्वारा, फिर परिभाषा के द्वाराid
  2. fmap (p . q) = (fmap p) . (fmap q)
    • अगर हमारे पास है Nothing
      • fmap (p . q) Nothing= Nothingभाग 1 द्वारा
      • (fmap p) . (fmap q) $ Nothing= (fmap p) $ Nothing= Nothingभाग 1 के दो अनुप्रयोगों द्वारा
    • अगर हमारे पास है Just x
      • fmap (p . q) (Just x)= Just ((p . q) x)= Just (p (q x))भाग 2 से, फिर की परिभाषा से.
      • (fmap p) . (fmap q) $ (Just x)= (fmap p) $ (Just (q x))= Just (p (q x))भाग दो के दो अनुप्रयोगों द्वारा

-1

"उपरोक्त कोड में बग से सावधान रहें; मैंने केवल इसे सही साबित किया है, इसे आज़माया नहीं है।" - डोनाल्ड नथ

एक आदर्श दुनिया में, प्रोग्रामर परिपूर्ण होते हैं और गलतियां नहीं करते हैं, इसलिए कोई बग नहीं हैं।

एक संपूर्ण दुनिया में, कंप्यूटर वैज्ञानिक और गणितज्ञ भी परिपूर्ण हैं, और गलतियाँ भी नहीं करते हैं।

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


5
यूनिट परीक्षणों में गलतियाँ भी हो सकती हैं। इससे भी महत्वपूर्ण बात, परीक्षण केवल कीड़े की उपस्थिति दिखा सकते हैं - उनकी अनुपस्थिति कभी नहीं। जैसा कि @Ingo ने अपने जवाब में कहा था, वे बहुत पवित्रता की जाँच करते हैं और सबूतों को अच्छी तरह से पूरक करते हैं, लेकिन वे उनके लिए प्रतिस्थापन नहीं हैं।
डोभाल
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.