नल के बिना भाषाओं के लिए सबसे अच्छा स्पष्टीकरण


225

हर बार जब प्रोग्रामर अशक्त त्रुटियों / अपवादों के बारे में शिकायत कर रहे होते हैं, तो कोई पूछता है कि हम अशक्त किए बिना क्या करते हैं।

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

  • डिफ़ॉल्ट रूप से संदर्भ / संकेत होने की अवांछनीयता अशक्त होगी
  • विकल्प कैसे काम करते हैं, जिसमें अशक्त मामलों की जांच करना आसान है
    • पैटर्न मिलान और
    • मौद्रिक बोध
  • वैकल्पिक समाधान जैसे संदेश खाना शून्य
  • (अन्य पहलू जो मुझे याद थे)

11
यदि आप कार्यात्मक-प्रोग्रामिंग या एफ # के लिए इस प्रश्न में टैग जोड़ते हैं तो आप कुछ शानदार उत्तर प्राप्त करने के लिए बाध्य हैं।
स्टीफन स्वेनसेन

मैंने कार्यात्मक प्रोग्रामिंग टैग जोड़ा क्योंकि विकल्प-प्रकार मिलीलीटर दुनिया से आया था। मैं इसके बजाय F # (बहुत विशिष्ट) चिह्नित नहीं करता। BTW किसी को वर्गीकरण शक्तियों के साथ एक शायद-प्रकार या विकल्प-प्रकार टैग जोड़ने की आवश्यकता है।
रोमन ए। टेचर

4
ऐसे विशिष्ट टैग की बहुत कम आवश्यकता है, मुझे संदेह है। टैग मुख्य रूप से लोगों को प्रासंगिक प्रश्न खोजने के लिए अनुमति देते हैं (उदाहरण के लिए, "प्रश्न जिनके बारे में मुझे बहुत कुछ पता है, और उत्तर देने में सक्षम होंगे", और "कार्यात्मक-प्रोग्रामिंग" वहां बहुत मददगार है। लेकिन "null" या "कुछ" विकल्प-प्रकार "बहुत कम उपयोगी होते हैं। कुछ लोगों को" विकल्प-प्रकार "टैग की निगरानी करने की संभावना होती है जो उन सवालों की तलाश में होते हैं जिनका वे जवाब दे सकते हैं;)
jalf

आइए यह मत भूलो कि अशक्त होने का एक मुख्य कारण यह है कि कंप्यूटर सिद्धांत स्थापित करने के लिए मजबूती से बंधे हुए हैं। नल सेट सिद्धांत के सभी में सबसे महत्वपूर्ण सेटों में से एक है। इसके बिना संपूर्ण एल्गोरिदम टूट जाएगा। उदाहरण के लिए- मर्ज सॉर्ट करें। इसमें कई बार आधी सूची तोड़ना शामिल है। क्या होगा यदि सूची 7 आइटम लंबी है? पहले आप इसे 4 और 3 में विभाजित करें। फिर 2, 2, 2 और 1. फिर 1, 1, 1, 1, 1, 1, 1, और .... अशक्त! नल का एक उद्देश्य है, सिर्फ एक जिसे आप व्यावहारिक रूप से नहीं देखते हैं। यह सैद्धांतिक क्षेत्र के लिए अधिक मौजूद है।
स्टेवेंडेसु

6
@steven_desu - मैं असहमत हूं। 'अशक्त' भाषाओं में, आप एक खाली सूची [] का संदर्भ ले सकते हैं, और एक अशक्त सूची संदर्भ भी। यह प्रश्न दोनों के बीच भ्रम से संबंधित है।
16

जवाबों:


433

मुझे लगता है कि अशक्त क्यों नहीं है की संक्षिप्त सारांश यह है कि निरर्थक राज्यों का प्रतिनिधित्व नहीं किया जाना चाहिए

मान लीजिए मैं एक दरवाजा बना रहा हूं। यह तीन राज्यों में से एक में हो सकता है: खुला, बंद लेकिन खुला, और बंद और बंद। अब मैं इसकी तर्ज पर मॉडलिंग कर सकता था

class Door
    private bool isShut
    private bool isLocked

और यह स्पष्ट है कि मेरे तीन राज्यों को इन दो बूलियन चर में कैसे मैप किया जाए। लेकिन यह एक चौथा, अवांछित राज्य उपलब्ध है isShut==false && isLocked==true:। क्योंकि जिन प्रकारों को मैंने अपने प्रतिनिधित्व के रूप में चुना है, वे इस स्थिति को स्वीकार करते हैं, मुझे यह सुनिश्चित करने के लिए मानसिक रूप से प्रयास करना होगा कि वर्ग कभी भी इस स्थिति में न आए (शायद स्पष्ट रूप से एक अपरिवर्तनीय कोडिंग द्वारा)। इसके विपरीत, अगर मैं बीजीय डेटा प्रकारों या चेक किए गए गणनाओं के साथ एक भाषा का उपयोग कर रहा था जो मुझे परिभाषित करने देता है

type DoorState =
    | Open | ShutAndUnlocked | ShutAndLocked

तब मैं परिभाषित कर सकता था

class Door
    private DoorState state

और कोई चिंता नहीं है। टाइप सिस्टम यह सुनिश्चित करेगा कि इसमें class Doorहोने के उदाहरण के लिए केवल तीन संभावित राज्य हैं । यह वह प्रकार है जो सिस्टम में अच्छे हैं - स्पष्ट रूप से संकलन-समय पर त्रुटियों के एक पूरे वर्ग को सत्तारूढ़ करना।

इसके साथ समस्या nullयह है कि प्रत्येक संदर्भ प्रकार को अपने स्थान में यह अतिरिक्त स्थिति मिलती है जो आमतौर पर अवांछित है। एक stringचर वर्णों का कोई भी अनुक्रम हो सकता है, या यह यह पागल अतिरिक्त nullमूल्य हो सकता है जो मेरी समस्या डोमेन में मैप नहीं करता है। एक Triangleऑब्जेक्ट में तीन Points होते हैं, जो स्वयं के पास होते हैं Xऔर Yमान होते हैं, लेकिन दुर्भाग्य से Points या Triangleस्वयं ही यह पागल शून्य मान हो सकता है जो कि मेरे द्वारा काम कर रहे ग्राफिंग डोमेन के लिए अर्थहीन है। आदि।

जब आप एक संभवतः-गैर-मौजूद मूल्य को मॉडल करने का इरादा रखते हैं, तो आपको इसे स्पष्ट रूप से चुनना चाहिए। अगर मैं लोगों को मॉडल करने का इरादा रखता हूं, तो यह है कि हर Personएक के पास कुछ FirstNameहै LastName, लेकिन केवल कुछ लोग हैं MiddleName, तो मैं कुछ कहना चाहूंगा

class Person
    private string FirstName
    private Option<string> MiddleName
    private string LastName

जहां stringयहां गैर-व्यर्थ प्रकार माना जाता है। तब NullReferenceExceptionकिसी के नाम की लंबाई की गणना करने की कोशिश करने पर कोई अप्रत्याशित आक्रमण नहीं होता है और न ही कोई अप्रत्याशित एस। प्रकार प्रणाली यह सुनिश्चित करती है कि किसी भी कोड के MiddleNameहोने की संभावना के लिए खातों से निपटनाNone , जबकि किसी भी कोड से निपटने के लिए FirstNameसुरक्षित रूप से मान सकते हैं कि वहाँ एक मूल्य है।

उदाहरण के लिए, उपरोक्त प्रकार का उपयोग करके, हम इस मूर्खतापूर्ण कार्य को लेखक कर सकते हैं:

let TotalNumCharsInPersonsName(p:Person) =
    let middleLen = match p.MiddleName with
                    | None -> 0
                    | Some(s) -> s.Length
    p.FirstName.Length + middleLen + p.LastName.Length

कोई चिंता नहीं है। इसके विपरीत, स्ट्रिंग जैसे प्रकारों के लिए अशक्त संदर्भों वाली भाषा में, फिर ग्रहण करना

class Person
    private string FirstName
    private string MiddleName
    private string LastName

आप सामान की तरह संलेखन समाप्त करते हैं

let TotalNumCharsInPersonsName(p:Person) =
    p.FirstName.Length + p.MiddleName.Length + p.LastName.Length

यदि आने वाली व्यक्ति वस्तु में सब कुछ गैर-अशक्त होने या न होने का आवेश नहीं होता है, तो यह सामने आता है

let TotalNumCharsInPersonsName(p:Person) =
    (if p.FirstName=null then 0 else p.FirstName.Length)
    + (if p.MiddleName=null then 0 else p.MiddleName.Length)
    + (if p.LastName=null then 0 else p.LastName.Length)

या हो सकता है

let TotalNumCharsInPersonsName(p:Person) =
    p.FirstName.Length
    + (if p.MiddleName=null then 0 else p.MiddleName.Length)
    + p.LastName.Length

मानाकि p पहले / अंतिम हैं, लेकिन मध्य अशक्त हो सकता है, या शायद आप ऐसे चेक करते हैं जो विभिन्न प्रकार के अपवादों को फेंकते हैं, या आप क्या जानते हैं। इन सभी पागल कार्यान्वयन विकल्पों और चीजों को फसल के बारे में सोचने के लिए क्योंकि यह बेवकूफ प्रतिनिधित्व योग्य-मूल्य है जो आपको नहीं चाहिए या आवश्यकता नहीं है।

नल आमतौर पर अनावश्यक जटिलता जोड़ता है। जटिलता सभी सॉफ़्टवेयर का दुश्मन है, और आपको जब भी उचित हो जटिलता को कम करने का प्रयास करना चाहिए।

(अच्छी तरह से ध्यान दें कि इन सरल उदाहरणों के लिए और भी अधिक जटिलता है। भले ही एक FirstNameनहीं हो सकता है null, एक stringप्रतिनिधित्व कर सकता है ""(खाली स्ट्रिंग), जो शायद एक व्यक्ति का नाम भी नहीं है जिसे हम मॉडल करना चाहते हैं। जैसे, गैर के साथ भी। अशक्त तार, यह अभी भी मामला हो सकता है कि हम "अर्थहीन मूल्यों का प्रतिनिधित्व" कर रहे हैं। फिर, आप रनटाइम पर इनवियर्स और सशर्त कोड के माध्यम से या तो टाइप सिस्टम (उदाहरण के लिए एक NonEmptyStringप्रकार) का उपयोग करके यह लड़ाई चुन सकते हैं । उत्तरार्द्ध शायद बीमार-सलाह है ("अच्छे" प्रकार आम ऑपरेशन के सेट पर अक्सर "बंद" होते हैं, और जैसे NonEmptyStringबंद नहीं होता है.SubString(0,0)), लेकिन यह डिज़ाइन स्पेस में अधिक पॉइंट प्रदर्शित करता है। दिन के अंत में, किसी भी प्रकार की प्रणाली में, कुछ जटिलता होती है जो छुटकारा पाने में बहुत अच्छी होगी, और अन्य जटिलता जो छुटकारा पाने के लिए आंतरिक रूप से कठिन है। इस विषय की कुंजी यह है कि लगभग हरप्रकार प्रणाली, "डिफ़ॉल्ट रूप से अशक्त संदर्भों" से "गैर-अशक्त संदर्भों को डिफ़ॉल्ट रूप से" परिवर्तन लगभग हमेशा एक सरल परिवर्तन है जो टाइप प्रणाली को जटिलता से जूझने और कुछ प्रकार की त्रुटियों और निरर्थक राज्यों से बाहर निकालने में काफी हद तक बेहतर बनाता है। इसलिए यह बहुत पागल है कि कई भाषाएं इस त्रुटि को बार-बार दोहराती रहती हैं।)


31
पुन: नाम - वास्तव में। और हो सकता है कि आप एक ऐसे दरवाजे के बारे में सोचते हों, जो खुला लटक रहा हो, लेकिन ताला डेडबोल से चिपका हो, जिससे दरवाजा बंद न हो। दुनिया में जटिलता बहुत है। कुंजी आपके सॉफ़्टवेयर में "वर्ल्ड स्टेट्स" और "प्रोग्राम स्टेट्स" के बीच मैपिंग को लागू करते समय अधिक जटिलता जोड़ने के लिए नहीं है ।
ब्रायन

59
क्या, आपने कभी दरवाजे बंद नहीं किए हैं?
जोशुआ

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

67
@akaphenom: धन्यवाद, लेकिन ध्यान दें कि सभी लोग बीयर नहीं पीते हैं (मैं एक गैर-पीने वाला हूं)। लेकिन मैं सराहना करता हूं कि आप केवल कृतज्ञता का संचार करने के लिए दुनिया के एक सरलीकृत मॉडल का उपयोग कर रहे हैं, इसलिए मैं आपके विश्व-मॉडल की त्रुटिपूर्ण मान्यताओं के बारे में अधिक नहीं समझूंगा। : पी (वास्तविक दुनिया में इतनी जटिलता! :))
ब्रायन

4
अजीब तरह से, इस दुनिया में 3-राज्य-दरवाजे हैं! उनका उपयोग कुछ होटलों में टॉयलेट-डोर के रूप में किया जाता है। पुश-बटन अंदर से एक कुंजी के रूप में कार्य करता है, जो बाहर से दरवाजा लॉक करता है। जैसे ही कुंडी बोल्ट चलता है, यह स्वचालित रूप से अनलॉक हो जाता है।
कोमोनड

65

विकल्प प्रकारों के बारे में अच्छी बात यह नहीं है कि वे वैकल्पिक हैं। यह है कि अन्य सभी प्रकार नहीं हैं

कभी-कभी , हमें एक प्रकार की "अशक्त" स्थिति का प्रतिनिधित्व करने में सक्षम होना चाहिए। कभी-कभी हमें "नो वैल्यू" विकल्प के साथ-साथ अन्य संभावित मानों का भी प्रतिनिधित्व करना पड़ता है जो एक चर ले सकता है। तो एक भाषा जो सपाट हो जाती है, वह थोड़ी अपंग हो जाती है।

लेकिन अक्सर , हमें इसकी आवश्यकता नहीं होती है, और ऐसी "अशक्त" स्थिति की अनुमति देने से केवल अस्पष्टता और भ्रम पैदा होता है: हर बार जब मैं .NET में एक संदर्भ प्रकार चर का उपयोग करता हूं, तो मुझे विचार करना होगा कि यह अशक्त हो सकता है

अक्सर, यह वास्तव में कभी नहीं होगा भी अशक्त , क्योंकि प्रोग्रामर कोड को संरचना देता है ताकि ऐसा कभी न हो सके। लेकिन संकलक यह सत्यापित नहीं कर सकता है, और हर बार जब आप इसे देखते हैं, तो आपको अपने आप से पूछना होगा "क्या यह शून्य हो सकता है? क्या मुझे यहां नल की जांच करने की आवश्यकता है?"

आदर्श रूप से, कई मामलों में जहां अशक्त होने का कोई मतलब नहीं है, इसे अनुमति नहीं दी जानी चाहिए

यह .NET में हासिल करने के लिए मुश्किल है, जहां लगभग सब कुछ शून्य हो सकता है। आपको उस कोड के लेखक पर भरोसा करना होगा जिसे आप 100% अनुशासित और सुसंगत कह रहे हैं और स्पष्ट रूप से प्रलेखित है कि क्या अशक्त हो सकता है और क्या नहीं, या आपको पागल हो जाना चाहिए और सब कुछ जांचना होगा ।

हालाँकि, यदि प्रकार अशक्त नहीं हैं डिफ़ॉल्ट रूप से अशक्त नहीं हैं, तो आपको यह जाँचने की आवश्यकता नहीं है कि वे शून्य हैं या नहीं। आप जानते हैं कि वे कभी भी अशक्त नहीं हो सकते, क्योंकि कंपाइलर / टाइप चेकर आपके लिए इसे लागू करता है।

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

जैसा कि दूसरों ने उल्लेख किया है, उदाहरण के लिए C # या Java में, शून्य का मतलब दो चीजों में से एक हो सकता है:

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

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


और दूसरे अर्थ में, हम चाहते हैं कि कंपाइलर हमें चेतावनी दें (रोकें?) अगर हम पहले शून्यता के लिए जाँच किए बिना ऐसे चर तक पहुंचने का प्रयास करते हैं। आगामी null / non-null C # फ़ीचर (अंत में!) ब्लॉगs.msdn.microsoft.com/dotnet/2017/11/15/… के
Schneider

44

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

वे तो पता चलता है कि यह एक बहुत साफ विचार हो सकता है अगर आप के लिए गैर nullability लागू पर जाने के सभी मूल्यों, जो किया जा सकता है अगर आप की तरह एक अवधारणा को जोड़ने OptionयाMaybe प्रकारों का प्रतिनिधित्व करते हैं जो हमेशा एक परिभाषित मूल्य नहीं हो सकते हैं। यह हास्केल द्वारा लिया गया दृष्टिकोण है।

यह सब अच्छा सामान है! लेकिन यह एक ही प्रभाव को प्राप्त करने के लिए स्पष्ट रूप से अशक्त / गैर-अशक्त प्रकारों के उपयोग को रोकता नहीं है। क्यों, विकल्प, अभी भी एक अच्छी बात है? सब के बाद, स्काला नल मान (है का समर्थन करता है है , तो यह जावा पुस्तकालयों के साथ काम कर सकते हैं करने के लिए), लेकिन समर्थन करता है Optionsके रूप में अच्छी तरह से।

प्र तो पूरी तरह से एक भाषा से नल हटाने में सक्षम होने से परे क्या लाभ हैं?

ए। रचना

यदि आप अशक्त कोड से एक भोली अनुवाद करते हैं

def fullNameLength(p:Person) = {
  val middleLen =
    if (null == p.middleName)
      p.middleName.length
    else
      0
  p.firstName.length + middleLen + p.lastName.length
}

विकल्प-जागरूक कोड के लिए

def fullNameLength(p:Person) = {
  val middleLen = p.middleName match {
    case Some(x) => x.length
    case _ => 0
  }
  p.firstName.length + middleLen + p.lastName.length
}

ज्यादा अंतर नहीं है! लेकिन यह भी विकल्प का उपयोग करने के लिए एक भयानक तरीका है ... यह दृष्टिकोण बहुत साफ है:

def fullNameLength(p:Person) = {
  val middleLen = p.middleName map {_.length} getOrElse 0
  p.firstName.length + middleLen + p.lastName.length
}

या और भी:

def fullNameLength(p:Person) =       
  p.firstName.length +
  p.middleName.map{length}.getOrElse(0) +
  p.lastName.length

जब आप विकल्पों की सूची के साथ काम करना शुरू करते हैं, तो यह और भी बेहतर हो जाता है। कल्पना करें कि सूची peopleस्वयं वैकल्पिक है:

people flatMap(_ find (_.firstName == "joe")) map (fullNameLength)

यह कैसे काम करता है?

//convert an Option[List[Person]] to an Option[S]
//where the function f takes a List[Person] and returns an S
people map f

//find a person named "Joe" in a List[Person].
//returns Some[Person], or None if "Joe" isn't in the list
validPeopleList find (_.firstName == "joe")

//returns None if people is None
//Some(None) if people is valid but doesn't contain Joe
//Some[Some[Person]] if Joe is found
people map (_ find (_.firstName == "joe")) 

//flatten it to return None if people is None or Joe isn't found
//Some[Person] if Joe is found
people flatMap (_ find (_.firstName == "joe")) 

//return Some(length) if the list isn't None and Joe is found
//otherwise return None
people flatMap (_ find (_.firstName == "joe")) map (fullNameLength)

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


8
+1, यह जोर देने के लिए एक अच्छा बिंदु है। एक परिशिष्ट: हास्केल-भूमि में, flatMapको (>>=)मोनड्स के लिए "बाइंड" ऑपरेटर कहा जाता है। यह सही है, हास्केलर flatMapचीजों को इतना पसंद करते हैं कि हम इसे अपनी भाषा के लोगो में डालते हैं।
सीए मैककैन

1
+1 उम्मीद है कि Option<T>कभी भी, कभी भी अशक्त नहीं होगा। अफसोस की बात है, स्काला उह है, फिर भी जावा :-) से जुड़ा हुआ है (दूसरी तरफ, अगर स्काला जावा के साथ अच्छा नहीं खेलता, तो इसका इस्तेमाल कौन करेगा? ओओ)

करने के लिए काफी आसान है: 'सूची (शून्य)। हेडऑप्शन'। ध्यान दें कि इसका अर्थ
केविन राइट

4
जब से आप रचना के बारे में कहे हैं, मैं वास्तव में आपको पसंद नहीं करता, क्योंकि अन्य लोगों का उल्लेख नहीं था।
रोमन ए। टेचर

बेहतरीन उदाहरणों के साथ बेहतरीन जवाब!
thSoft

38

चूंकि लोग इसे याद कर रहे हैं: null यह अस्पष्ट है।

ऐलिस की जन्म तारीख है null । इसका क्या मतलब है?

बॉब की मृत्यु तिथि है null । इसका क्या मतलब है?

एक "उचित" व्याख्या यह हो सकती है कि एलिस की जन्म-तिथि मौजूद है, लेकिन अज्ञात है, जबकि बॉब की तारीख-मृत्यु मौजूद नहीं है (बॉब अभी भी जीवित है)। लेकिन हमें अलग-अलग जवाब क्यों मिले?


एक और समस्या: nullएक किनारे का मामला है।

  • है null = null?
  • है nan = nan?
  • है inf = inf?
  • है +0 = -0?
  • है +0/0 = -0/0?

उत्तर आमतौर पर "हां", "नहीं", "हां", "हां", "नहीं", "हां" क्रमशः हैं। पागल "गणितज्ञों" NaN "अशक्तता" कहते हैं और कहते हैं कि यह खुद के बराबर है। एसक्यूएल नल को कुछ भी नहीं के बराबर मानता है (इसलिए वे NaN की तरह व्यवहार करते हैं)। जब आप एक ही डेटाबेस कॉलम में column store, what 0, और NaN को स्टोर करने की कोशिश करते हैं तो एक चमत्कार होता है (2 53 NaN हैं, जिनमें से आधे "नकारात्मक" हैं)।

मामले को बदतर बनाने के लिए, डेटाबेस अलग-अलग होते हैं कि वे NULL के साथ कैसा व्यवहार करते हैं, और उनमें से अधिकांश सुसंगत नहीं हैं (देखें अवलोकन के लिए SQLite में NULL हैंडलिंग )। यह बहुत भयानक है।


और अब अनिवार्य कहानी के लिए:

मैंने हाल ही में एक (sqlite3) डेटाबेस तालिका को पाँच स्तंभों के साथ डिज़ाइन किया है a NOT NULL, b, id_a, id_b NOT NULL, timestamp। क्योंकि यह एक सामान्य स्कीमा है जिसे काफी मनमाने ऐप्स के लिए एक सामान्य समस्या को हल करने के लिए डिज़ाइन किया गया है, इसमें दो विशिष्टताएं हैं:

UNIQUE(a, b, id_a)
UNIQUE(a, b, id_b)

id_aकेवल मौजूदा ऐप डिज़ाइन के साथ संगतता के लिए मौजूद है (आंशिक रूप से क्योंकि मैं एक बेहतर समाधान के साथ नहीं आया हूं), और नए ऐप में इसका उपयोग नहीं किया गया है। वजह से जिस तरह से शून्य एसक्यूएल में काम करता है, मैं सम्मिलित कर सकते हैं (1, 2, NULL, 3, t)और (1, 2, NULL, 4, t)और पहली विशिष्टता बाधा नहीं का उल्लंघन (क्योंकि(1, 2, NULL) != (1, 2, NULL) )।

यह विशेष रूप से इस वजह से काम करता है कि कैसे NULL अधिकांश डेटाबेस पर एक विशिष्ट बाधा में काम करता है (संभवतः इसलिए यह "वास्तविक दुनिया" स्थितियों को मॉडल करना आसान है, उदाहरण के लिए, किसी भी दो लोगों के पास एक ही सामाजिक सुरक्षा संख्या नहीं हो सकती है, लेकिन सभी लोगों के पास एक नहीं है)।


एफडब्ल्यूआईडब्ल्यू, पहले अपरिभाषित व्यवहार को लागू किए बिना, सी ++ संदर्भ "शून्य" को इंगित नहीं कर सकता है, और यह असम्बद्ध संदर्भ सदस्य चर के साथ एक वर्ग का निर्माण करना संभव नहीं है (यदि अपवाद फेंक दिया गया है, तो निर्माण विफल रहता है)।

सिडेनोट: कभी-कभी आप पारस्परिक रूप से अनन्य पॉइंटर्स चाहते हैं (यानी उनमें से केवल एक ही गैर-पूर्ण हो सकता है), उदाहरण के लिए एक काल्पनिक आईओएस में type DialogState = NotShown | ShowingActionSheet UIActionSheet | ShowingAlertView UIAlertView | Dismissed। इसके बजाय, मुझे सामान पसंद करने के लिए मजबूर किया जाता है assert((bool)actionSheet + (bool)alertView == 1)


वास्तविक गणितज्ञ "NaN" की अवधारणा का उपयोग नहीं करते हैं, हालांकि, बाकी का आश्वासन दिया गया है।
नोल्डोरिन

@ नोल्डोरिन: वे करते हैं, लेकिन वे "अनिश्चित रूप" शब्द का उपयोग करते हैं।
IJ कैनेडी

@IJKennedy: यह एक अलग कॉलेज है, जिसे मैं अच्छी तरह से जानता हूं कि आपका धन्यवाद। कुछ 'एनएएन' अनिश्चित फॉर्म का प्रतिनिधित्व कर सकते हैं, लेकिन चूंकि एफपीए प्रतीकात्मक तर्क नहीं करता है, इसलिए इसे अनिश्चित फॉर्म के साथ समीकरण करना काफी भ्रामक है!
नोल्डोरिन

इसमें गलत क्या है assert(actionSheet ^ alertView)? या अपनी भाषा XOR नहीं कर सकते?
बिल्ली

16

संदर्भ / संकेत होने की अवांछनीयता डिफ़ॉल्ट रूप से अशक्त हो सकती है।

मुझे नहीं लगता कि यह अशक्तताओं के साथ मुख्य मुद्दा है, शून्य के साथ मुख्य मुद्दा यह है कि वे दो चीजों का मतलब कर सकते हैं:

  1. संदर्भ / सूचक असंवैधानिक है: यहां समस्या सामान्य रूप से परिवर्तनशीलता के समान है। एक के लिए, अपने कोड का विश्लेषण करना अधिक कठिन हो जाता है।
  2. चर शून्य होने का वास्तव में कुछ मतलब होता है: यह ऐसा मामला है जो विकल्प प्रकार वास्तव में औपचारिकता करता है।

विकल्प के प्रकारों का समर्थन करने वाली भाषाएँ आम तौर पर असमान रूप से भिन्न चर के उपयोग को भी मना या हतोत्साहित करती हैं।

पैटर्न मिलान के रूप में अशक्त मामलों की जांच में आसानी के लिए रणनीतियों सहित विकल्प कैसे काम करते हैं।

प्रभावी होने के लिए, विकल्प प्रकारों को सीधे भाषा में समर्थित होने की आवश्यकता होती है। अन्यथा उन्हें अनुकरण करने के लिए बहुत सारे बॉयलर-प्लेट कोड लगते हैं। पैटर्न-मिलान और टाइप-इनविज़न दो कुंजी भाषा सुविधाएँ हैं, जिनके साथ काम करना आसान विकल्प विकल्प हैं। उदाहरण के लिए:

एफ # में:

//first we create the option list, and then filter out all None Option types and 
//map all Some Option types to their values.  See how type-inference shines.
let optionList = [Some(1); Some(2); None; Some(3); None]
optionList |> List.choose id //evaluates to [1;2;3]

//here is a simple pattern-matching example
//which prints "1;2;None;3;None;".
//notice how value is extracted from op during the match
optionList 
|> List.iter (function Some(value) -> printf "%i;" value | None -> printf "None;")

हालाँकि, विकल्प प्रकारों के सीधे समर्थन के बिना जावा जैसी भाषा में, हमारे पास कुछ ऐसा होगा:

//here we perform the same filter/map operation as in the F# example.
List<Option<Integer>> optionList = Arrays.asList(new Some<Integer>(1),new Some<Integer>(2),new None<Integer>(),new Some<Integer>(3),new None<Integer>());
List<Integer> filteredList = new ArrayList<Integer>();
for(Option<Integer> op : list)
    if(op instanceof Some)
        filteredList.add(((Some<Integer>)op).getValue());

वैकल्पिक समाधान जैसे संदेश खाना शून्य

ऑब्जेक्टिव-सी का "मैसेज ईटिंग नाइल" इतना समाधान नहीं है जितना कि नाक की जाँच के सिर-दर्द को हल्का करने का प्रयास है। मूल रूप से, एक अशक्त वस्तु पर एक विधि को लागू करने की कोशिश करते समय एक रनटाइम अपवाद को फेंकने के बजाय, अभिव्यक्ति इसके बजाय अशक्त करने के लिए मूल्यांकन करती है। अविश्वास को निलंबित करना, ऐसा लगता है जैसे प्रत्येक उदाहरण विधि से शुरू होता है if (this == null) return null;। लेकिन तब सूचना हानि होती है: आप नहीं जानते कि क्या विधि शून्य है क्योंकि यह वैध रिटर्न मूल्य है, या क्योंकि वस्तु वास्तव में अशक्त है। यह अपवाद को निगलने जैसा बहुत कुछ है, और पहले से उल्लिखित अशक्त के साथ मुद्दों को संबोधित करने के लिए कोई प्रगति नहीं करता है।


यह एक पालतू जानवर है लेकिन c # शायद ही कोई सी-लाइक भाषा है।
रोमन ए। टेचर

4
मैं यहां जावा के लिए जा रहा था, क्योंकि C # में शायद एक अच्छा समाधान होगा ... लेकिन मैं आपके peeve की सराहना करता हूं, लोगों को वास्तव में "सी-प्रेरित वाक्य रचना के साथ एक भाषा" का क्या मतलब है। मैंने आगे बढ़कर "सी-लाइक" स्टेटमेंट को बदल दिया।
स्टीफन स्वेंसन

Linq के साथ, सही है। मैं सी # के बारे में सोच रहा था और उस पर ध्यान नहीं दिया।
रोमन ए। टेचर

1
हाँ, ग प्रेरित सिंटैक्स के साथ ज्यादातर, लेकिन मुझे लगता है कि मैंने भी प्रोग्रामिंग प्रोग्रामिंग भाषाओं के बारे में सुना है जैसे अजगर / रूबी बहुत कम के साथ सी जैसे सिंटैक्स को कार्यात्मक प्रोग्रामर द्वारा सी-जैसे कहा जाता है।
रोमन ए। टेचर

11

असेंबली ने हमें ऐसे पते दिए जिन्हें अनकैप्ड पॉइंटर्स के रूप में भी जाना जाता है। C ने उन्हें सीधे टाइप किए गए पॉइंटर्स के रूप में मैप किया, लेकिन सभी टाइप किए गए पॉइंटर्स के साथ संगत एक अद्वितीय पॉइंटर वैल्यू के रूप में अल्गोल के नल को पेश किया। C में null के साथ बड़ा मुद्दा यह है कि चूंकि हर पॉइंटर null हो सकता है, कोई भी मैन्युअल चेक के बिना किसी पॉइंटर को सुरक्षित रूप से उपयोग नहीं कर सकता है।

उच्च-स्तरीय भाषाओं में, अशक्त होना अजीब है क्योंकि यह वास्तव में दो अलग-अलग धारणाएं व्यक्त करता है:

  • यह बताना कि कुछ अपरिभाषित है
  • यह बताना कि कुछ वैकल्पिक है

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

दूसरा मामला वैकल्पिकता है और इसे स्पष्ट रूप से एक विकल्प प्रकार के साथ उदाहरण के लिए प्रदान किया गया है ।


मान लीजिए कि हम एक परिवहन कंपनी में हैं और हमें अपने ड्राइवरों के लिए एक कार्यक्रम बनाने में मदद करने के लिए एक एप्लिकेशन बनाने की आवश्यकता है। प्रत्येक ड्राइवर के लिए, हम कुछ informations जैसे कि ड्राइविंग लाइसेंस उनके पास है और आपातकालीन स्थिति में कॉल करने के लिए फोन नंबर।

सी में हम कर सकते हैं:

struct PhoneNumber { ... };
struct MotorbikeLicence { ... };
struct CarLicence { ... };
struct TruckLicence { ... };

struct Driver {
  char name[32]; /* Null terminated */
  struct PhoneNumber * emergency_phone_number;
  struct MotorbikeLicence * motorbike_licence;
  struct CarLicence * car_licence;
  struct TruckLicence * truck_licence;
};

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

OCaml में, समान कोड इस तरह दिखेगा:

type phone_number = { ... }
type motorbike_licence = { ... }
type car_licence = { ... }
type truck_licence = { ... }

type driver = {
  name: string;
  emergency_phone_number: phone_number option;
  motorbike_licence: motorbike_licence option;
  car_licence: car_licence option;
  truck_licence: truck_licence option;
}

आइए अब कहते हैं कि हम सभी ड्राइवरों के नाम उनके ट्रक लाइसेंस नंबर के साथ प्रिंट करना चाहते हैं।

सी में:

#include <stdio.h>

void print_driver_with_truck_licence_number(struct Driver * driver) {
  /* Check may be redundant but better be safe than sorry */
  if (driver != NULL) {
    printf("driver %s has ", driver->name);
    if (driver->truck_licence != NULL) {
      printf("truck licence %04d-%04d-%08d\n",
        driver->truck_licence->area_code
        driver->truck_licence->year
        driver->truck_licence->num_in_year);
    } else {
      printf("no truck licence\n");
    }
  }
}

void print_drivers_with_truck_licence_numbers(struct Driver ** drivers, int nb) {
  if (drivers != NULL && nb >= 0) {
    int i;
    for (i = 0; i < nb; ++i) {
      struct Driver * driver = drivers[i];
      if (driver) {
        print_driver_with_truck_licence_number(driver);
      } else {
        /* Huh ? We got a null inside the array, meaning it probably got
           corrupt somehow, what do we do ? Ignore ? Assert ? */
      }
    }
  } else {
    /* Caller provided us with erroneous input, what do we do ?
       Ignore ? Assert ? */
  }
}

OCaml में होगा कि:

open Printf

(* Here we are guaranteed to have a driver instance *)
let print_driver_with_truck_licence_number driver =
  printf "driver %s has " driver.name;
  match driver.truck_licence with
    | None ->
        printf "no truck licence\n"
    | Some licence ->
        (* Here we are guaranteed to have a licence *)
        printf "truck licence %04d-%04d-%08d\n"
          licence.area_code
          licence.year
          licence.num_in_year

(* Here we are guaranteed to have a valid list of drivers *)
let print_drivers_with_truck_licence_numbers drivers =
  List.iter print_driver_with_truck_licence_number drivers

जैसा कि आप इस तुच्छ उदाहरण में देख सकते हैं, सुरक्षित संस्करण में कुछ भी जटिल नहीं है:

  • यह तंग करने वाला है।
  • आपको बहुत बेहतर गारंटी मिलती है और किसी भी प्रकार की अशक्त जांच की आवश्यकता नहीं होती है।
  • संकलक ने सुनिश्चित किया कि आप विकल्प के साथ सही तरीके से निपटते हैं

जबकि सी में, आप बस एक अशक्त जांच और उछाल भूल सकता है ...

नोट: ये कोड नमूने जहां संकलित नहीं किए गए हैं, लेकिन मुझे आशा है कि आपको विचार मिल गए हैं।


मैंने कभी इसकी कोशिश नहीं की है, लेकिन en.wikipedia.org/wiki/Cyclone_%28programming_language%29 सी के लिए गैर-शून्य बिंदुओं को अनुमति देने का दावा करता है।
रोमन ए। टेचर

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

मेरा मानना ​​है NULL कि "संदर्भ जो कुछ भी इंगित नहीं कर सकता है" का आविष्कार कुछ अल्गोल भाषा के लिए किया गया था (विकिपीडिया सहमत हैं, en.wikipedia.org/wiki/Null_pointer#Null_pointer देखें )। लेकिन निश्चित रूप से यह संभावना है कि असेंबली प्रोग्रामर ने अपने संकेत को एक अमान्य एड्रेस के लिए आरम्भ किया (पढ़ें: Null = 0)।

1
@ स्टीफन: हम शायद एक ही बात मतलब था। मेरे लिए वे बिना सोचे-समझे चीजों के उपयोग को हतोत्साहित या निषिद्ध करते हैं क्योंकि अपरिभाषित चीजों पर चर्चा करने का कोई मतलब नहीं है क्योंकि हम उनके साथ कुछ भी समझदार या उपयोगी नहीं कर सकते हैं। इसका कोई हित नहीं होगा।
18

2
@tc के रूप में। कहते हैं, null का विधानसभा से कोई लेना-देना नहीं है। विधानसभा में, प्रकार आमतौर पर अशक्त नहीं होते हैं । एक सामान्य-उद्देश्य रजिस्टर में लोड किया गया मान शून्य हो सकता है या यह कुछ गैर-शून्य पूर्णांक हो सकता है। लेकिन यह कभी भी अशक्त नहीं हो सकता। यहां तक ​​कि अगर आप एक स्मृति पते को एक रजिस्टर में लोड करते हैं, तो अधिकांश सामान्य आर्किटेक्चर पर, "अशक्त सूचक" का कोई अलग प्रतिनिधित्व नहीं है। यह उच्च-स्तरीय भाषाओं में पेश की गई अवधारणा है, जैसे कि C
jalf

5

Microsoft रिसर्च में एक इंटरस्टिंग परियोजना है, जिसे कहा जाता है

युक्ति #

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


4

.NET बैकग्राउंड से आते हुए, मुझे लगा कि null के पास एक बिंदु है, जो कि उपयोगी है। जब तक मुझे संरचना का पता नहीं चला और बॉयलरप्लेट कोड के बहुत से बचने से उनके साथ काम करना कितना आसान था। टोनी होरे2009 में QCon लंदन में बोलते हुए ने अशक्त संदर्भ का आविष्कार करने के लिए माफी मांगी । उसे उद्धृत करने के लिए:

मैं इसे अपनी अरबों डॉलर की गलती कहता हूं। यह 1965 में अशक्त संदर्भ का आविष्कार था। उस समय, मैं ऑब्जेक्ट ओरिएंटेड भाषा (ALGOL W) में संदर्भ के लिए पहला व्यापक प्रकार का सिस्टम डिजाइन कर रहा था। मेरा लक्ष्य यह सुनिश्चित करना था कि संकलक द्वारा स्वचालित रूप से किए गए चेकिंग के साथ सभी संदर्भों का उपयोग पूरी तरह से सुरक्षित होना चाहिए। लेकिन मैं एक अशक्त संदर्भ में डालने के प्रलोभन का विरोध नहीं कर सकता था, केवल इसलिए कि इसे लागू करना इतना आसान था। इससे असंख्य त्रुटियां, कमजोरियां और सिस्टम क्रैश हो गए हैं, जिसने पिछले चालीस वर्षों में संभवतः एक अरब डॉलर का दर्द और क्षति पहुंचाई है। हाल के वर्षों में, Microsoft में PREIFT और PREIFT जैसे कई प्रोग्राम विश्लेषणियों का उपयोग संदर्भों की जांच करने के लिए किया गया है, और चेतावनी दी गई है कि अगर कोई जोखिम है तो वे गैर-अशक्त हो सकते हैं। Spec # जैसी हालिया प्रोग्रामिंग भाषाओं ने गैर-अशक्त संदर्भों के लिए घोषणाएं प्रस्तुत की हैं। यह समाधान है, जिसे मैंने 1965 में खारिज कर दिया था।

इस सवाल को भी प्रोग्रामर पर देखें


3

रॉबर्ट Nystrom यहाँ एक अच्छा लेख प्रदान करता है:

http://journal.stuffwithstuff.com/2010/08/23/void-null-maybe-and-nothing/

उनकी मैगी प्रोग्रामिंग भाषा की अनुपस्थिति और विफलता के लिए समर्थन जोड़ते समय उनकी विचार प्रक्रिया का वर्णन करना ।


1

मैंने हमेशा अशक्त (या शून्य) को मूल्य की अनुपस्थिति के रूप में देखा है

कभी-कभी आप यही चाहते हैं, कभी-कभी आप ऐसा नहीं करते हैं। यह उस डोमेन पर निर्भर करता है जिसके साथ आप काम कर रहे हैं। यदि अनुपस्थिति सार्थक है: कोई मध्य नाम नहीं है, तो आपका आवेदन तदनुसार कार्य कर सकता है। दूसरी ओर यदि अशक्त मान नहीं होना चाहिए: पहला नाम अशक्त है, तो डेवलपर को लौकिक 2 am फोन कॉल मिलता है।

मैंने कोड को ओवरलोड और ओवर-कॉम्प्लेक्स में नल के लिए चेक के साथ भी देखा है। मेरे लिए इसका मतलब दो चीजों में से
एक है : ए) एप्लीकेशन ट्री में एक बग ऊपर उच्च
ख) खराब / अधूरा डिजाइन

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

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


हाय @Jon, यह थोड़ा कठिन है आप यहाँ का पालन करें। मुझे अंत में एहसास हुआ कि "विशेष / अजीब" मूल्यों से आप संभवतः जावास्क्रिप्ट के 'अपरिभाषित' या IEEE के 'NaN' जैसे कुछ का मतलब है। लेकिन इसके अलावा, आप वास्तव में ओपी द्वारा पूछे गए किसी भी प्रश्न को संबोधित नहीं करते हैं। और यह कथन कि "अशक्त होने की जाँच करने के लिए अशक्त शायद सबसे उपयोगी धारणा है" कुछ निश्चित रूप से गलत है। विकल्प प्रकार एक अच्छी तरह से माना जाता है, नल के लिए सुरक्षित प्रकार का विकल्प है।
स्टीफन स्वेंसन

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

0

वेक्टर भाषाएँ कभी-कभी अशक्त न होने के साथ दूर हो सकती हैं।

खाली वेक्टर इस मामले में एक टाइप्ड नल के रूप में कार्य करता है।


मुझे लगता है कि मैं समझता हूं कि आप किस बारे में बात कर रहे हैं लेकिन क्या आप कुछ उदाहरणों को सूचीबद्ध कर सकते हैं? विशेष रूप से एक संभवतः शून्य मान के लिए कई कार्यों को लागू करने के लिए?
रोमन ए। टेचर

अच्छी तरह से एक खाली वेक्टर में एक वेक्टर रूपांतरण लागू करने से एक और खाली वेक्टर में परिणाम होता है। FYI करें, SQL ज्यादातर एक वेक्टर भाषा है।
जोशुआ

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