यदि नल खराब हैं, तो क्या उपयोग किया जाना चाहिए जब कोई मूल्य सार्थक रूप से अनुपस्थित हो सकता है?


26

यह उन नियमों में से एक है जो बार-बार दोहरा रहे हैं और यह मुझे परेशान करता है।

नल बुराई हैं और जब भी संभव हो बचना चाहिए

लेकिन, लेकिन - मेरे भोलेपन से, मुझे चीखने दो - कभी-कभी एक मूल्य सार्थक रूप से अनुपस्थित हो सकता है!

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

प्रत्येक मोड़ के बाद सर्वर क्लाइंट साइड JS कोड में अपडेट की एक सूची प्रसारित करता है। अद्यतन वर्ग:

public class GameUpdate
{
    public Player player;
    // other stuff
}

इस वर्ग को JSON से क्रमबद्ध किया जाता है और कनेक्टेड खिलाड़ियों को भेजा जाता है।

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

बेशक, मैं विरासत का उपयोग करके इस कोड को "ठीक" कर सकता था:

public class GameUpdate
{
    // stuff
}

public class GamePlayerUpdate : GameUpdate
{
    public Player player;
    // other stuff
}

हालाँकि, मैं यह देखने में विफल हूं कि यह दो कारणों से किसी भी सुधार का कैसे है:

  • जेएस कोड अब केवल एक परिभाषित संपत्ति के रूप में प्लेयर के बिना एक ऑब्जेक्ट प्राप्त करेगा, जो वैसा ही है जैसे कि यह शून्य था क्योंकि दोनों मामलों में चेक की आवश्यकता होती है यदि मूल्य मौजूद है या अनुपस्थित है;
  • GameUpdate वर्ग में एक और अशक्त क्षेत्र को जोड़ा जाना चाहिए, मुझे इस desing के साथ जारी रखने के लिए कई विरासत का उपयोग करने में सक्षम होना होगा - लेकिन एमआई अपने आप में बुराई है (अधिक अनुभवी प्रोग्रामर के अनुसार) और इससे भी महत्वपूर्ण बात, सी # नहीं है ऐसा है तो मैं इसका इस्तेमाल नहीं कर सकता।

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

क्या आप मुझे यह मुद्दा समझा सकते हैं?


2
क्या यह प्रश्न जावास्क्रिप्ट के लिए विशिष्ट है? कई भाषाओं में इस उद्देश्य के लिए एक वैकल्पिक प्रकार होता है, लेकिन मुझे नहीं पता कि js में से एक है (हालांकि आप एक बना सकते हैं) या अगर यह json के साथ काम करेगा (हालांकि यह आपके कोड में सभी के बजाय बुरा अशांति का एक हिस्सा है
रिचर्ड टिंगल

9
वह नियम सिर्फ झूठा है। मैं थोड़ा हैरान हूं कि कोई उस धारणा का समर्थन करेगा। कुछ मामलों में अशक्त करने के लिए कुछ अच्छे विकल्प हैं। एक लापता मूल्य का प्रतिनिधित्व करने के लिए नल वास्तव में अच्छा है। ऐसे यादृच्छिक इंटरनेट नियम न दें, जो आपको अपने तर्कपूर्ण निर्णय पर भरोसा करने से रोकते हैं।
यूएसआर

1
@usr - मैं पूरी तरह से सहमत हूं। नल आपका दोस्त है। IMHO, लापता मूल्य का प्रतिनिधित्व करने का कोई स्पष्ट तरीका नहीं है। यह आपको बग ढूंढने में मदद कर सकता है, यह आपको त्रुटि की स्थिति को संभालने में मदद कर सकता है, यह आपको क्षमा के लिए भीख माँगने में मदद कर सकता है (कोशिश / पकड़)। क्या पसंद नहीं करना?!
स्कूबा स्टीव

4
सॉफ्टवेयर डिजाइन करने के पहले नियमों में से एक है "हमेशा नल की जांच करें"। यहां तक ​​कि यह एक मुद्दा भी है, जो मेरे लिए अजीब है।
मैट

1
@ मैट - बिल्कुल। मैं एक डिज़ाइन पैटर्न क्यों बनाऊंगा जब वास्तव में, नल आपके मूल टूलबॉक्स में होना अच्छी बात है। ग्रैहम क्या सुझाव दे रहा था, IMHO, मुझे ओवर-इंजीनियरिंग के रूप में मारता है।
स्कूबा स्टीव

जवाबों:


20

अशक्त की तुलना में बहुत सारी चीजें बेहतर हैं।

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

जब यह करने के लिए चाल का एहसास हो रहा है। नल वास्तव में आपके चेहरे पर उड़ने में अच्छा है, लेकिन केवल तभी जब आप इसके लिए जाँच किए बिना इसे बंद कर दें। यह समझाने में अच्छा नहीं है कि क्यों।

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

मोनाड्स ध्वनि की कल्पना करते हैं, लेकिन यहां वे सब कर रहे हैं जिससे आप यह स्पष्ट कर सकते हैं कि संग्रह के लिए मान्य आकार 0 और 1. हैं, यदि आपके पास उन्हें है, तो उन पर विचार करें।

इनमें से कोई भी अशक्त की तुलना में आपके इरादे को स्पष्ट करने का बेहतर काम करता है। यही सबसे महत्वपूर्ण अंतर है।

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

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

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

अशक्त के साथ समस्या यह है कि बहुत अधिक चीजों का मतलब हो सकता है। तो यह सिर्फ जानकारी को नष्ट करने के लिए समाप्त होता है।


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

आपको RFC3.14159 के साथ इस छिपी हुई महंगी चीज़ का अनुपालन करने के लिए कहा गया है जिसमें कहा गया है कि संदेशों के बीच एक मार्कर होना चाहिए। मार्कर बिल्कुल प्रकाश नहीं होना चाहिए। यह ठीक एक नाड़ी के लिए पिछले होना चाहिए। एक ही रंग का प्रकाश सामान्य रूप से चमकता है कि समय की एक ही राशि।

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

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

यह बहुरूपी समाधान है। सिस्टम को यह भी जानने की जरूरत नहीं है कि कुछ भी बदल गया है।


यह वास्तव में अच्छा है अगर आप इसे बंद कर सकते हैं। एक मूल्य में एक धारणा के एक सार्थक अस्वीकृति को कूटबद्ध करके आपको प्रश्न को बदलने के लिए मजबूर करने की आवश्यकता नहीं है।

अगर मैं तुम्हें 1 सेब दे दूं तो तुम कितने सेब दोगे? -1। क्योंकि मैंने कल से आपको 2 सेब दिए। यहां प्रश्न की धारणा, "आप मुझ पर एहसान करेंगे", एक नकारात्मक संख्या के साथ खारिज कर दिया गया है। हालांकि प्रश्न गलत था, सार्थक जानकारी इस उत्तर में एन्कोडेड है। इसे सार्थक तरीके से अस्वीकार करने से प्रश्न को बदलने के लिए मजबूर नहीं किया जाता है।

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

आपकी समस्या यह है कि, आमतौर पर, अपडेट खिलाड़ियों से आते हैं। लेकिन आपने एक अपडेट की आवश्यकता की खोज की है जो खिलाड़ी 1 या खिलाड़ी 2 से नहीं आता है। आपको एक अपडेट की आवश्यकता है जो कहता है कि समय समाप्त हो गया है। न तो खिलाड़ी यह कह रहा है तो आपको क्या करना चाहिए? खिलाड़ी अशक्त छोड़ना बहुत लुभावना लगता है। लेकिन यह जानकारी को नष्ट कर देता है। आप एक ब्लैक होल में ज्ञान को एनकोड करने की कोशिश कर रहे हैं।

खिलाड़ी फ़ील्ड आपको यह नहीं बताता है कि कौन स्थानांतरित हुआ। आपको लगता है कि यह करता है लेकिन यह समस्या साबित होती है कि झूठ है। खिलाड़ी फ़ील्ड आपको बताता है कि अद्यतन कहां से आया है। आपका समय समाप्त हो गया अद्यतन खेल घड़ी से आ रहा है। मेरी सिफारिश घड़ी को वह श्रेय देती है जिसका वह हकदार है और playerक्षेत्र का नाम कुछ इस तरह से बदल देता है source

इस तरह यदि सर्वर बंद हो रहा है और गेम को निलंबित करना है तो यह एक अपडेट भेज सकता है जो कि हो रहा है और रिपोर्ट को अपडेट के स्रोत के रूप में सूचीबद्ध करता है।

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

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


9
"एक संग्रह लौटाएं अगर इसमें केवल 0 या 1 तत्व हैं" - मुझे खेद है लेकिन मुझे नहीं लगता कि मुझे यह मिल गया है :( मेरे पीओवी से ऐसा करने से (ए) अनावश्यक अमान्य राज्यों को वर्ग में जोड़ता है (बी) को दस्तावेजीकरण की आवश्यकता है संग्रह में अधिक से अधिक 1 तत्व होना चाहिए (c) वैसे भी समस्या को हल करने के लिए प्रतीत नहीं होता है, क्योंकि अगर संग्रह में इसके एकमात्र तत्व को रखने से पहले यह आवश्यक है कि यह किसी भी तरह से आवश्यक है, तो
चबाना

2
@gaazkam देखें? मैंने तुमसे कहा था कि यह लोगों को परेशान करता है। फिर भी यह वही है जो आप हर बार खाली स्ट्रिंग का उपयोग करने के लिए कर रहे हैं।
कैंडिड_ऑरेंज

16
क्या होता है जब एक वैध मान होता है जो खाली स्ट्रिंग या सेट होता है?
ब्लरफुल

5
वैसे भी @gaazkam, आप यह देखने के लिए स्पष्ट रूप से जांच नहीं करते हैं कि संग्रह में आइटम हैं या नहीं। खाली सूची पर लूपिंग (या मैपिंग) एक सुरक्षित क्रिया है जिसका कोई प्रभाव नहीं है।
रबरडुक

3
मुझे पता है कि आप इस सवाल का जवाब दे रहे हैं कि किसी को क्या करना चाहिए क्योंकि उसे वापस लौटने के बजाय क्या करना चाहिए लेकिन मैं वास्तव में इन बिंदुओं से बहुत असहमत हूं। हालाँकि, जब हम भाषा विशिष्ट नहीं हैं और हर नियम को तोड़ने का एक कारण है, जिसे मैं अस्वीकार करता हूं
Liath

15

nullवास्तव में यहाँ समस्या नहीं है। समस्या यह है कि अधिकांश मौजूदा प्रोग्रामिंग भाषाएँ गुम जानकारी से कैसे निपटती हैं; वे इस समस्या को गंभीरता से नहीं लेते हैं (या कम से कम समर्थन और सुरक्षा प्रदान नहीं करते हैं क्योंकि अधिकांश पृथ्वी को नुकसान से बचने की आवश्यकता होती है)। यह उन सभी समस्याओं की ओर ले जाता है जिनसे हमने डरना सीखा है (Javas NullPointerException, Segfaults, ...)।

आइए देखें कि बेहतर तरीके से उस मुद्दे से कैसे निपटा जाए।

अन्य ने नल-ऑब्जेक्ट पैटर्न का सुझाव दिया । जबकि मुझे लगता है कि यह कभी-कभी लागू होता है, वहाँ बहुत सारे परिदृश्य होते हैं जहाँ सिर्फ एक आकार-फिट-सभी डिफ़ॉल्ट मान नहीं होते हैं। उन मामलों में, संभवतः-अनुपस्थित जानकारी के उपभोक्ताओं को स्वयं के लिए यह तय करने में सक्षम होना चाहिए कि क्या है यदि वह जानकारी गायब है।

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

एक स्पष्ट, सामान्य प्रकार जो संभावित अनुपस्थिति को दर्शाता है, इससे निपटने का एक और तरीका है। जैसे जावा में है java.util.Optional। यह काम कर सकता है:
एक परियोजना या कंपनी के स्तर पर, आप नियम / दिशानिर्देश स्थापित कर सकते हैं

  • केवल उच्च गुणवत्ता वाले 3 पार्टी पुस्तकालयों का उपयोग करें
  • हमेशा केjava.util.Optional बजाय का उपयोग करेंnull

आपकी भाषा समुदाय / पारिस्थितिकी तंत्र इस समस्या को संकलक / दुभाषिया से बेहतर मानने के अन्य तरीकों के साथ आ सकता है।


क्या आप विस्तार से बता सकते हैं कि जावा ऑप्टिकल्स नेल्स से बेहतर कैसे हैं? जैसा कि मैंने दस्तावेज़ीकरण पढ़ा , वैकल्पिक से एक मूल्य प्राप्त करने की कोशिश करना एक अपवाद पैदा करता है यदि मूल्य अनुपस्थित है; इसलिए ऐसा लगता है कि हम एक ही जगह पर हैं: एक जाँच आवश्यक है और अगर हम एक भूल करते हैं, तो हम खराब हो जाते हैं?
गज़कम

3
@ बजाजकम आप सही हैं, यह संकलन-समय की सुरक्षा प्रदान नहीं करता है। इसके अलावा, वैकल्पिक ही एक और मुद्दा हो सकता है। लेकिन यह स्पष्ट है (चर की तुलना में संभवत: अशक्त / डॉक्टर टिप्पणी है)। यह केवल एक वास्तविक लाभ देता है यदि, पूरे कोडबेस के लिए, एक कोडिंग नियम स्थापित किया गया है। आप चीजों को अशक्त नहीं कर सकते। यदि जानकारी अनुपस्थित हो सकती है, तो वैकल्पिक का उपयोग करें। वह बल खोजता है। हमेशा की तरह अशक्त-जांच करता है तो बनने के लिए कॉलOptional.isPresent
marstato


10

मैं इस कथन में कोई योग्यता नहीं देखता कि अशक्त बुरा है। मैंने इसके बारे में चर्चा की जाँच की और उन्होंने केवल मुझे अधीर और चिड़चिड़ा बना दिया।

नल का गलत उपयोग किया जा सकता है, "जादू मूल्य" के रूप में, जब यह वास्तव में कुछ का मतलब होता है। लेकिन आपको वहां पहुंचने के लिए कुछ सुंदर मुड़ प्रोग्रामिंग करनी होगी।

अशक्त का अर्थ "वहाँ नहीं" होना चाहिए, जो कि एक तर्क है अगर नहीं (अभी तक) या (पहले से ही) चला गया है या प्रदान नहीं किया गया है। यह एक राज्य नहीं है, पूरी चीज गायब है। एक OO सेटिंग में जहां चीजें बनाई जाती हैं और हर समय नष्ट हो जाती हैं जो किसी भी राज्य से अलग वैध, सार्थक स्थिति है।

अशक्त के साथ आप रनटाइम पर स्मैकडाउन हो जाते हैं, जहां आप कुछ प्रकार के सुरक्षित नल विकल्प के साथ संकलन समय पर स्मैकडाउन हो जाते हैं।

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

जागरूक होने के लिए एक मुद्दा है। एक बहु-थ्रेडेड संदर्भ में आपको शून्य के लिए चेक प्रदर्शन करने से पहले पहले एक स्थानीय चर को असाइन करके दौड़ की स्थिति के खिलाफ सुरक्षा करना होगा।

अशक्त के साथ समस्या यह है कि कई चीजों के लिए दूर का मतलब हो सकता है। तो यह सिर्फ जानकारी को नष्ट करने के लिए समाप्त होता है।

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

संपादित करें:

मैं कार्यात्मक प्रोग्रामिंग के बारे में उनके अन्य उत्तरों में से एक में वीडियो कैंडिड_ऑरेंज से आधा जुड़ा हुआ हूं और यह बहुत दिलचस्प है। मैं कुछ परिदृश्यों में इसकी उपयोगिता देख सकता हूं। कार्यात्मक दृष्टिकोण जो मैंने अब तक देखा है, कोड के चारों ओर उड़ने वाले टुकड़ों के साथ, आमतौर पर जब ओओ सेटिंग में उपयोग किया जाता है, तो मुझे ऐंठन होती है। लेकिन मुझे लगता है कि यह अपनी जगह है। कहीं। और मुझे लगता है कि ऐसी भाषाएं होंगी जो एक अच्छा काम करती हैं जो मैं एक भ्रमित करने वाला अनुभव करता हूं जब भी मैं इसे C # में मुठभेड़ करता हूं, तो ऐसी भाषाएं जो इसके बजाय एक स्वच्छ और व्यावहारिक मॉडल प्रदान करती हैं।

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

किसी भी अवधारणा का गलत तरीके से इस्तेमाल किया जा सकता है। जहां से मैं खड़ा हूं, प्रस्तुत "समाधान" नल के उपयुक्त उपयोग के लिए कोई विकल्प नहीं हैं। वे एक और दुनिया से अवधारणाएं हैं।


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

3
@candied_orange आप किसी भी वैकल्पिक-प्रकार के बारे में वही तर्क बना सकते हैं: Noneप्रतिनिधित्व करता है ...।
डेडुप्लिकेटर

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

2
अशक्त गलती है क्योंकि आपके पास कुछ नहीं के अर्थ को एनकोड करने का मौका था। कुछ भी नहीं की अपनी समझ के बिना कुछ भी नहीं की तत्काल निपटने की मांग करता है। 0, null, NaN, खाली स्ट्रिंग, खाली सेट, शून्य, शून्य ऑब्जेक्ट, लागू नहीं, अपवाद लागू नहीं, कई कई रूपों में कुछ भी नहीं आता है। आपको यह नहीं भूलना है कि आप अभी क्या जानते हैं क्योंकि आप अभी जो कुछ भी जानते हैं उससे निपटना नहीं चाहते हैं। अगर मैं आपसे -1 का वर्गमूल लेने के लिए कहूं तो आप या तो एक अपवाद फेंक सकते हैं मुझे बताएं कि यह असंभव है और सब कुछ भूल जाएं या काल्पनिक संख्याओं का आविष्कार करें और जो आप जानते हैं उसे संरक्षित करें।
candied_orange

1
@MartinMaat आप इसे ऐसे बनाते हैं जैसे किसी फ़ंक्शन को हर बार आपकी अपेक्षाओं का उल्लंघन करने पर अपवाद छोड़ देना चाहिए। यह बिल्कुल सही नहीं है। अक्सर निपटने के लिए कोने के मामले होते हैं। यदि आप वास्तव में यह नहीं देख सकते हैं कि इसे
पढ़िए

7

आपका प्रश्न।

लेकिन, लेकिन - मेरे भोलेपन से, मुझे चीखने दो - कभी-कभी एक मूल्य सार्थक रूप से अनुपस्थित हो सकता है!

पूरी तरह से आपके साथ वहाँ पर। हालाँकि, कुछ कैविएट हैं जिन्हें मैं एक सेकंड में प्राप्त करूँगा। लेकिन पहले:

नल बुराई हैं और जब भी संभव हो बचना चाहिए।

मुझे लगता है कि यह एक सुविचारित लेकिन अतिरंजित बयान है।

नल दुष्ट नहीं हैं। वे एक एंटीपैटर्न नहीं हैं। वे ऐसी कोई चीज नहीं हैं, जिसे हर कीमत पर टाला जाए। हालांकि, नल त्रुटि के लिए प्रवृत्त हैं (अशक्त से जाँच करने में विफल)।

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

यदि अशक्त बुराई है, तो फिर सरणी इंडेक्स और सी ++ पॉइंटर्स हैं। वे नहीं हैं। वे अपने आप को पैर में मारना आसान समझते हैं, लेकिन उन्हें हर कीमत पर टाला नहीं जाता है।

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


आपका समाधान

जबकि मैं एक वैश्विक नियम के रूप में अशक्त के उपयोग पर आपकी राय से सहमत हूं; मैं इस विशेष मामले में अशक्त के आपके उपयोग से सहमत नहीं हूं।

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

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

यदि ये अपडेट सार्थक रूप से भिन्न हैं (जो वे हैं), तो आपको दो अलग-अलग अपडेट प्रकारों की आवश्यकता है।

उदाहरण के लिए, संदेशों पर विचार करें। आपके पास त्रुटि संदेश और IM चैट संदेश हैं। लेकिन जब वे बहुत समान डेटा (एक स्ट्रिंग संदेश और एक प्रेषक) हो सकते हैं, तो वे उसी तरह व्यवहार नहीं करते हैं। उन्हें अलग-अलग वर्गों के रूप में, अलग-अलग उपयोग किया जाना चाहिए।

अब आप जो कुछ भी कर रहे हैं, वह प्लेयर प्रॉपर्टी की अशक्तता के आधार पर दो प्रकार के अपडेट के बीच प्रभावी रूप से अंतर कर रहा है। यह IM संदेश और त्रुटि कोड के अस्तित्व के आधार पर एक त्रुटि संदेश के बीच निर्णय लेने के बराबर है। यह काम करता है, लेकिन यह सबसे अच्छा तरीका नहीं है।

  • जेएस कोड अब केवल एक परिभाषित संपत्ति के रूप में प्लेयर के बिना एक ऑब्जेक्ट प्राप्त करेगा, जो वैसा ही है जैसे कि यह शून्य था क्योंकि दोनों मामलों में चेक की आवश्यकता होती है यदि मूल्य मौजूद है या अनुपस्थित है;

आपका तर्क बहुरूपता के दुखों पर निर्भर करता है। यदि आपके पास कोई ऐसा तरीका है जो किसी GameUpdateवस्तु को लौटाता है , तो इसे आम तौर पर कभी भी अंतर करने की परवाह नहीं करनी चाहिए GameUpdate, PlayerGameUpdateया NewlyDevelopedGameUpdate

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

यह आपके तर्क को प्रस्तुत करता है। अच्छे व्यवहार में, आपको कभी भी ध्यान नहीं देना चाहिए कि आपकी GameUpdateवस्तु में खिलाड़ी है या नहीं। यदि आप Playerएक की संपत्ति तक पहुँचने की कोशिश कर रहे हैं GameUpdate, तो आप कुछ अतार्किक काम कर रहे हैं।

टाइप की गई भाषाएं जैसे C # भी आपको प्रयास करने की अनुमति नहीं देगा क्योंकि Playerसंपत्ति का उपयोग करने के लिए बस संपत्ति नहीं है। जावास्क्रिप्ट अपने दृष्टिकोण में बहुत अधिक ढीला है (और इसे पूर्वसंचालन की आवश्यकता नहीं है) इसलिए यह आपको सही करने के लिए परेशान नहीं करता है और इसके बजाय रनटाइम पर उड़ा है।GameUpdateGameUpdate

  • GameUpdate वर्ग में एक और अशक्त क्षेत्र को जोड़ा जाना चाहिए, मुझे इस desing के साथ जारी रखने के लिए कई विरासत का उपयोग करने में सक्षम होना होगा - लेकिन एमआई अपने आप में बुराई है (अधिक अनुभवी प्रोग्रामर के अनुसार) और इससे भी महत्वपूर्ण बात, सी # नहीं है ऐसा है तो मैं इसका इस्तेमाल नहीं कर सकता।

आपको अशक्त गुणों के आधार पर कक्षाएं नहीं बनानी चाहिए। आपको कार्यात्मक रूप से अनूठे प्रकार के गेम अपडेट के आधार पर कक्षाएं बनानी चाहिए ।


सामान्य रूप से अशक्त मुद्दों से कैसे बचें?

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

1. वैसे भी अशक्त उपयोग करें।

यह सबसे आसान तरीका है। हालाँकि, आप अपने आप को एक टन के नल चेक के लिए साइन अप कर रहे हैं और (जब ऐसा करने में विफल रहे) अशक्त संदर्भ अपवादों का निवारण करें।

2. एक गैर-शून्य अर्थहीन मूल्य का उपयोग करें

यहाँ एक सरल उदाहरण है indexOf():

"abcde".indexOf("b"); // 1
"abcde".indexOf("f"); // -1

इसके बदले nullआपको मिलता है -1। तकनीकी स्तर पर, यह एक मान्य पूर्णांक मान है। हालांकि, एक नकारात्मक मूल्य एक निरर्थक उत्तर है क्योंकि अनुक्रमित सकारात्मक पूर्णांक होने की उम्मीद है।

तार्किक रूप से, इसका उपयोग केवल उन मामलों के लिए किया जा सकता है जहां रिटर्न प्रकार अधिक संभव मानों की अनुमति देता है, सार्थक रूप से लौटाए जा सकते हैं (indexOf = सकारात्मक पूर्णांक; int = नकारात्मक और सकारात्मक पूर्णांक)

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

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

3. इसके बजाय एक अपवाद फेंको।

नहीं, बस नहीं। अपवादों को तार्किक प्रवाह के लिए नियंत्रित नहीं किया जाना चाहिए।

कहा जा रहा है कि, कुछ ऐसे मामले हैं जहाँ आप (अशक्त) अपवाद को छिपाना नहीं चाहते, बल्कि एक उपयुक्त अपवाद दिखाना चाहते हैं। उदाहरण के लिए:

var existingPerson = personRepository.GetById(123);

यह एक PersonNotFoundException(या समान) फेंक सकता है जब आईडी 123 के साथ कोई व्यक्ति नहीं है।

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


5

नल क्यों खराब हैं इसका मतलब यह नहीं है कि लापता मूल्य को शून्य के रूप में एन्कोड किया गया है, लेकिन यह कि कोई भी संदर्भ मूल्य शून्य हो सकता है। यदि आपके पास C # है, तो आप शायद वैसे भी खो गए हैं। मुझे एक बिंदु आरओ कुछ वैकल्पिक नहीं दिखता है क्योंकि एक संदर्भ प्रकार पहले से ही वैकल्पिक है। लेकिन Java के लिए @Notnull एनोटेशन है, जिसे आप पैकेज स्तर पर सेट करने में सक्षम लगते हैं , फिर उन मानों के लिए जिन्हें आप मिस कर सकते हैं एनोटेशन @Nullable का उपयोग कर सकते हैं।


हाँ! समस्या एक निरर्थक डिफ़ॉल्ट है।
डेडुप्लिकेटर

मैं असहमत हूं। एक स्टाइल में प्रोग्रामिंग करना जो शून्य से बचता है, कम संदर्भ चर को अशक्त बनाता है, कम संदर्भ चर के लिए अग्रणी है जो अशक्त नहीं होना चाहिए। यह 100% नहीं है, लेकिन शायद 90% है, जो इसके लायक है IMO।
मोनिका

1

नल बुराई नहीं हैं, वहाँ कुछ है कि एक अशक्त की तरह लग रहा है कुछ से निपटने के बिना विकल्पों में से किसी को लागू करने के लिए वास्तव में कोई रास्ता नहीं है।

नल हालांकि खतरनाक हैं । किसी भी अन्य निम्न स्तर के निर्माण की तरह अगर आपको यह गलत लगता है तो आपको पकड़ने के लिए नीचे कुछ भी नहीं है।

मुझे लगता है कि अशक्त के खिलाफ बहुत सारे मान्य तर्क हैं:

  • यदि आप पहले से ही उच्च स्तर के डोमेन में हैं, तो निम्न-स्तरीय मॉडल का उपयोग करने की तुलना में सुरक्षित पैटर्न हैं।

  • जब तक आपको निम्न-स्तरीय प्रणाली के फायदों की आवश्यकता नहीं होती है और उपयुक्त सतर्क रहने के लिए तैयार किया जाता है, तब शायद आपको निम्न-स्तरीय भाषा का उपयोग नहीं करना चाहिए।

  • आदि ...

ये सभी मान्य हैं और आगे लिखने और बसने आदि के प्रयास नहीं करने को उस सलाह का पालन नहीं करने के एक सामान्य कारण के रूप में देखा जाता है। यह वास्तव में आश्चर्यजनक नहीं है कि बहुत सारे प्रोग्रामर इस निष्कर्ष पर पहुंचे हैं कि नल खतरनाक और आलसी (एक बुरा संयोजन!) हैं, लेकिन "सार्वभौमिक" सलाह के कई अन्य बिट्स की तरह वे भी सार्वभौमिक नहीं हैं क्योंकि वे शब्द हैं।

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


बात यह है, "सार्वभौमिक सलाह" को आमतौर पर "सार्वभौमिक रूप से" नहीं कहा जाता है जैसा कि लोग दावा करते हैं। यदि आपको ऐसी सलाह के मूल स्रोत मिलते हैं, तो वे आम तौर पर उन कैविट की बात करते हैं, जिनके बारे में अनुभवी प्रोग्रामर जानते हैं। क्या होता है, जब कोई और सलाह पढ़ता है, तो वे उन बातों को नजरअंदाज कर देते हैं और केवल "सार्वभौमिक सलाह" वाले हिस्से को याद करते हैं, इसलिए जब वे उस सलाह को दूसरों से जोड़ते हैं, तो यह मूल रूप से बहुत अधिक "सार्वभौमिक" होता है। ।
निकोल बोलस

1
@ निकोलबोलस, आप सही कह रहे हैं, लेकिन मूल स्रोत वह सलाह नहीं है जिसे ज्यादातर लोग उद्धृत करते हैं, यह रिलेडेड संदेश है। जिस बिंदु पर मैं काम करना चाहता था, वह केवल यह था कि बहुत सारे प्रोग्रामर को 'कभी एक्स, एक्स बुरी / बुरी / जो कुछ भी हो' कहा गया है और जैसा कि आप कहते हैं, यह बहुत सारे कैविटी को याद करता है। शायद उन्हें गिरा दिया गया था, शायद वे कभी मौजूद नहीं थे, अंतिम परिणाम समान है।
०४ बजे drjpizzle

0

किसी भी मूल्य को सुरक्षित रूप से एक्सेस करने के लिए जावा में एक Optionalस्पष्ट ifPresent+ लैम्ब्डा के साथ एक वर्ग है । इसे JS में भी बनाया जा सकता है:

यह StackOverflow प्रश्न देखें ।

वैसे: कई शुद्धतावादी इस प्रयोग को संदिग्ध पाएंगे, लेकिन मुझे ऐसा नहीं लगता। वैकल्पिक सुविधाएँ मौजूद हैं।


एक संदर्भ भी नहीं java.lang.Optionalहो सकता nullहै?
डेडुप्लिकेटर

@Deduplicator नहीं, Optional.of(null)एक अपवाद फेंक Optional.ofNullable(null)देंगे , दे देंगे Optional.empty()- वहाँ नहीं मूल्य।
जोप एगेनजेन

और Optional<Type> a = null?
डेडुप्लिकेटर

@Deduplicator सच है, लेकिन वहां आपको उपयोग करना चाहिए Optional<Optional<Type>> a = Optional.empty();;) - JS के दूसरे शब्दों में (और अन्य भाषाओं में) ऐसी चीजें संभव नहीं होंगी। डिफ़ॉल्ट मान के साथ स्केला करना होगा। जावा शायद एक एनम के साथ। जे एस मुझे शक है: Optional<Type> a = Optional.empty();
जोप इगेन

इसलिए, हम एक अप्रत्यक्ष और संभावित nullया खाली से स्थानांतरित हो गए हैं , दो से कुछ अतिरिक्त तरीकों से अंतर्निर्मित वस्तु पर। यह काफी उपलब्धि है।
Deduplicator

0

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

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

अभिव्यक्ति की उस मूलभूत कमी के आसपास हैक करने के लिए, कुछ समुदाय (जैसे स्काला समुदाय) शून्य का उपयोग नहीं करते हैं। स्काला में, यदि आप प्रकार का एक अशक्त मान चाहते हैं, तो आप प्रकार Tका एक तर्क लेते हैं Option[T], और यदि आप एक अशक्त मूल्य चाहते हैं, तो आप केवल एक लेते हैं T। यदि कोई आपके कोड में एक शून्य से गुजरता है, तो आपका कोड उड़ जाएगा। हालाँकि, यह माना जाता है कि कॉलिंग कोड छोटी गाड़ी कोड है। Optionहालांकि, शून्य के लिए एक बहुत अच्छा प्रतिस्थापन है, क्योंकि आम नल-हैंडलिंग पैटर्न को लाइब्रेरी फ़ंक्शंस में .map(f)या जैसे निकाला जाता है .getOrElse(someDefault)

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