आपको मूल्य के आधार पर संदर्भ के आधार पर जानना होगा

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

मुझे उस दिन की याद है जब मैंने इस विषय पर पढ़ा था और मैं जिस भी स्रोत से गुज़री थी वह पिछले एक के विपरीत था। इसकी ठोस समझ पाने में कुछ समय लगा। मेरे पास कोई विकल्प नहीं था क्योंकि यदि आप एक सॉफ्टवेयर इंजीनियर हैं तो यह एक मौलिक विषय है।

मैं कुछ हफ्ते पहले एक बुरा बग में भाग गया और एक लेख लिखने का फैसला किया ताकि अन्य लोगों को इस पूरी बात का पता लगाने में आसानी हो।

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

सभी अवधारणाओं को समझने के लिए हालांकि हम कुछ गो और पर्ल उदाहरणों का उपयोग करेंगे।

पूरे विषय को समझने के लिए आपको 3 अलग-अलग चीजों को समझना होगा:

  • भाषा में अंतर्निहित डेटा संरचनाएं कैसे लागू की जाती हैं (ऑब्जेक्ट्स, आदिम प्रकार, उत्परिवर्तन)।
  • कैसे चर असाइनमेंट / कॉपी / पुनर्मूल्यांकन / तुलना कार्य
  • कैसे चर कार्यों के लिए पारित कर रहे हैं

डेटा प्रकारों को समझना

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

और हाँ रूबी में एक TrueClass है।

true.is_a? (TrueClass) => सच
3.is_a। (पूर्णांक) => सत्य
true.is_a (वस्तु) => सत्य
3.is_a? (वस्तु) => सत्य
TrueClass.is_a? (ऑब्जेक्ट) => सच
Integer.is_a? (ऑब्जेक्ट) => सच है

ये वस्तुएं परस्पर या अपरिवर्तनीय हो सकती हैं।

अपरिवर्तनीय का अर्थ है कि कोई भी तरीका नहीं है जिससे आप ऑब्जेक्ट को एक बार बना सकते हैं। किसी ऑब्जेक्ट के लिए दिए गए मान के लिए सिर्फ एक ही उदाहरण है और यह वही रहता है जो आप करते हैं।

रूबी में डिफ़ॉल्ट रूप से अपरिवर्तनीय वस्तु प्रकार हैं: बूलियन, न्यूमेरिक, नील और प्रतीक।

MRI में किसी ऑब्जेक्ट का object_id वैले के समान होता है जो C स्तर पर ऑब्जेक्ट का प्रतिनिधित्व करता है। अधिकांश प्रकार की वस्तुओं के लिए यह VALUE मेमोरी के किसी स्थान का एक संकेतक है जहां वास्तविक ऑब्जेक्ट डेटा संग्रहीत किया जाता है।

अब से हम वस्तु_ मेमोरी और मेमोरी एड्रेस को एक-दूसरे का उपयोग करेंगे।

आइए एक अपरिवर्तनीय प्रतीक और एक परिवर्तनशील स्ट्रिंग के लिए MRI में कुछ रूबी कोड चलाएं:

: प्रतीक.बिंदु_िद => 68०68६६.
: प्रतीक.बिंदु_िद => 68०68६६.
'string'.object_id => 70137215233780
'string'.object_id => 70137215215120

जैसा कि आप देखते हैं कि प्रतीक संस्करण समान मान के लिए समान object_id रखता है, स्ट्रिंग मान भिन्न मेमोरी पतों के होते हैं।

रूबी के विपरीत, जावास्क्रिप्ट में आदिम प्रकार हैं।

वे हैं - बूलियन, अशक्त, अपरिभाषित, स्ट्रिंग और संख्या।

बाकी डेटा प्रकार ऑब्जेक्ट (एरे, फ़ंक्शन, और ऑब्जेक्ट) की छतरी के नीचे जाते हैं। यहां कुछ भी फैंसी नहीं है, यह रूबी की तुलना में अधिक सीधा है।

[] उदाहरण एरियर => सच
[] उदाहरण वस्तु => सत्य
3 उदाहरण वस्तु => असत्य

परिवर्तनीय असाइनमेंट, कॉपी करना, पुनर्मूल्यांकन और तुलना

रूबी में प्रत्येक चर एक वस्तु का एक संदर्भ है (क्योंकि सब कुछ एक वस्तु है)।

a = 'स्ट्रिंग'
बी = ए
# यदि आप एक ही मूल्य के साथ फिर से असाइन करते हैं
a = 'स्ट्रिंग'
b => 'string' डालता है
एक == बी => सत्य # मान समान हैं
a.object_id == b.object_id => false # memory adr-s डालता है। अलग
# यदि आप दूसरे मान के साथ फिर से असाइन करते हैं
a = 'नया तार'
एक => 'नई स्ट्रिंग' डालता है
b => 'string' डालता है
एक == b => असत्य # मान अलग-अलग हैं
a.object_id == b.object_id => false # memory adr-s डालता है। अलग भी

जब आप एक चर असाइन करते हैं, तो यह एक वस्तु का संदर्भ है न कि वस्तु का। जब आप एक वस्तु b = की प्रतिलिपि बनाते हैं तो दोनों चर एक ही पते पर इंगित करेंगे।

इस व्यवहार को संदर्भ मान द्वारा प्रतिलिपि कहा जाता है।

रूबी और जावास्क्रिप्ट में कड़ाई से बोलते हुए सब कुछ मूल्य द्वारा कॉपी किया जाता है।

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

इसे संदर्भ द्वारा कॉपी किया जाएगा यदि 'नए स्ट्रिंग' के लिए पुन: असाइन करने के बाद, बी भी उसी पते पर इंगित करेगा और उसी 'नए स्ट्रिंग' मान होगा।

जब आप b = a घोषित करते हैं, तो a और b एक ही मेमोरी एड्रेस की ओर इशारा करते हैंA (a =) string ’) को पुन: असाइन करने के बाद, a और b अलग-अलग मेमोरी एड्रेस की ओर इशारा करते हैं

पूर्णांक जैसे अपरिवर्तनीय प्रकार के साथ:

a = १
बी = ए
a = १
डालता है => १
मूल्य से == बी => सच # तुलना डालता है
स्मृति adr द्वारा a.object_id == b.object_id => सही # तुलना डालता है।

जब आप एक ही पूर्णांक को पुन: असाइन करते हैं, तो मेमोरी एड्रेस एक ही रहता है क्योंकि किसी दिए गए पूर्णांक में हमेशा समान ऑब्जेक्ट_ड होता है।

जैसा कि आप देखते हैं जब आप किसी वस्तु की तुलना किसी दूसरे से करते हैं तो इसकी तुलना मूल्य से की जाती है। यदि आप जांचना चाहते हैं कि क्या वे समान वस्तु हैं तो आपको object_id का उपयोग करना होगा।

आइए जावास्क्रिप्ट संस्करण देखें:

var a = 'string';
var b = a;
a = 'string'; # एक ही मूल्य के लिए आश्वस्त है
console.log (क); => 'स्ट्रिंग'
console.log (ख); => 'स्ट्रिंग'
कंसोल.लॉग (a === b); => सत्य // मूल्य से तुलना
var a = [];
var b = a;
कंसोल.लॉग (a === b); => सत्य
a = [];
console.log (क); => []
console.log (ख); => []
कंसोल.लॉग (a === b); स्मृति पते द्वारा => झूठी // तुलना

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

खैर, बिल्कुल नहीं।

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

इसका मतलब है कि कोई भी चर कभी भी एक ही मेमोरी पते की ओर इशारा नहीं करेगा। यह भी महत्वपूर्ण है कि मूल्य स्वयं एक भौतिक मेमोरी स्थान में संग्रहीत है।

हमारे उदाहरण में, जब हम b = a की घोषणा करते हैं, तो b उसी ‘string’ मान के साथ एक अलग मेमोरी एड्रेस को इंगित करेगा। इसलिए आपको एक अलग मेमोरी एड्रेस की ओर संकेत करने की आवश्यकता नहीं है।

इसे मूल्य द्वारा कॉपी किया जाता है क्योंकि आपके पास केवल मूल्य पर मेमोरी पते तक कोई पहुंच नहीं है।

जब आप a = b घोषित करते हैं तो इसे a और b को अलग-अलग मेमोरी पतों पर दिया जाता है

आइए एक बेहतर उदाहरण देखें जहां यह सब मायने रखता है।

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

a = 'x'
बी = ए
a.concat ( 'y')
एक => 'xy' डालता है
b => 'xy' डालता है
b.concat ( 'z')
एक => 'xyz' डालता है
b => 'xyz' डालता है
a = 'z'
एक => 'z' डालता है
b => 'xyz' डालता है
एक [0] = 'y'
एक => 'y' डालता है
b => 'xyz' डालता है

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

आप कह सकते हैं कि आपने 'x' को असाइन किया है, लेकिन इसे मान द्वारा असाइन किया गया था इसलिए मेमोरी एड्रेस में 'x' का मान होता है, लेकिन आप इसे बदल नहीं सकते क्योंकि आपके पास इसका कोई संदर्भ नहीं है।

var a = 'x';
var b = a;
a.concat ( 'वाई');
console.log (क); => 'x'
console.log (ख); => 'x'
एक [0] = 'z';
console.log (क); => 'x';

जावास्क्रिप्ट वस्तुओं और कार्यान्वयन का व्यवहार रूबी की उत्परिवर्तनीय वस्तुओं की तरह ही है। दोनों प्रति संदर्भ मूल्य हो।

जावास्क्रिप्ट आदिम प्रकारों को मूल्य द्वारा कॉपी किया जाता है। व्यवहार वही है जैसे रूबी की अपरिवर्तनीय वस्तुएं जिन्हें संदर्भ मूल्य द्वारा कॉपी किया जाता है।

है ना?

फिर से, जब आप किसी चीज को मूल्य से कॉपी करते हैं तो इसका मतलब है कि आप मूल मान को बदल नहीं सकते हैं (क्योंकि स्मृति पते का कोई संदर्भ नहीं है)। लेखन कोड के दृष्टिकोण से यह एक ही चीज है जैसे अपरिवर्तनीय संस्थाएं जिन्हें आप म्यूट नहीं कर सकते हैं।

यदि आप रूबी और जावास्क्रिप्ट की तुलना केवल डेटा प्रकार से करते हैं जो ves डिफ़ॉल्ट रूप से भिन्न व्यवहार करता है, तो स्ट्रिंग है (इसलिए हमने उपरोक्त उदाहरणों में स्ट्रिंग का उपयोग किया है)।

रूबी में यह एक परिवर्तनशील वस्तु है और इसे संदर्भ मूल्य द्वारा कॉपी / पास किया जाता है जबकि जावास्क्रिप्ट में यह एक आदिम प्रकार है और मूल्य के लिए कॉपी / पास होता है।

जब आप क्लोन (प्रतिलिपि नहीं) करना चाहते हैं, तो आपको इसे दोनों भाषाओं में स्पष्ट रूप से करना होगा ताकि आप यह सुनिश्चित कर सकें कि मूल वस्तु को संशोधित नहीं किया जा सकता है:

a = {'नाम': 'केट'}
b = a.clone
b ['नाम'] = 'अन्ना'
एक => {: नाम => "केट"} डालता है
var a = {'name': 'केट'};
var b = {... a}; // नए ES6 सिंटैक्स के साथ
b ['नाम'] = 'अन्ना';
console.log (क); => {नाम: "केट"}

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

एक अन्य रिएक्ट (जावास्क्रिप्ट फ्रंट-एंड फ्रेमवर्क) है जहां आपको ऑब्जेक्ट आईडी के आधार पर तुलनात्मक कार्यों के रूप में अद्यतन करने के लिए हमेशा एक नया ऑब्जेक्ट पास करना होगा।

यह तेज़ है क्योंकि आपको ऑब्जेक्ट लाइन के माध्यम से लाइन में नहीं जाना है, यह देखने के लिए कि क्या इसे बदल दिया गया है।

कैसे चर कार्यों के लिए पारित कर रहे हैं

वेरिएबल्स को फ़ंक्शंस में पास करना उसी तरह से काम कर रहा है जैसे अधिकांश भाषाओं में समान डेटा प्रकारों के लिए कॉपी करना।

जावास्क्रिप्ट में आदिम प्रकार की नकल की जाती है और मूल्य द्वारा पारित की जाती है और वस्तुओं को संदर्भ मूल्य द्वारा कॉपी और पारित किया जाता है।

मुझे लगता है कि यही कारण है कि लोग केवल संदर्भ से पास या वैल्यू के बारे में बात करते हैं और कभी भी नकल का उल्लेख नहीं करते हैं। मुझे लगता है कि वे मान लेते हैं कि नकल उसी तरह काम करती है।

a = 'b'
def आउटपुट (स्ट्रिंग) # संदर्भ मान द्वारा पारित किया गया
  string = 'c' # reassigned तो कोई संदर्भ नहीं मूल
  तार डालता है
समाप्त
आउटपुट (a) => 'c'
a => 'b' डालता है
def output2 (string) # रेफरेंस वैल्यू से गुजरा
  string.concat ('c') # हम उस मान को बदलते हैं जो पते में बैठता है
  तार डालता है
समाप्त
आउटपुट (ए) => 'बीसी'
एक => 'बीसी' डालता है

अब जावास्क्रिप्ट में:

var a = 'b';
फ़ंक्शन आउटपुट (स्ट्रिंग) {// मान द्वारा पारित किया गया
  string = 'c'; // एक और मान के लिए पुन: असाइन किया गया
  console.log (स्ट्रिंग);
}
उत्पादन (क); => 'ग'
console.log (क); => 'बी'
function output2 (string) {// मान से पास किया गया
  string.concat ( 'ग'); // हम इसे संदर्भ के बिना संशोधित नहीं कर सकते
  console.log (स्ट्रिंग);
}
output2 (क); => 'बी'
console.log (क); => 'बी'

यदि आप जावास्क्रिप्ट में एक ऑब्जेक्ट (न कि एक आदिम प्रकार जैसे हमने किया) पास करते हैं, तो यह उसी तरह काम करता है जैसे कि रूबी उदाहरण।

अन्य भाषाएँ

हम पहले ही देख चुके हैं कि कैसे मान द्वारा कॉपी / पास और संदर्भ मूल्य काम से कॉपी / पास। अब हम देखेंगे कि संदर्भ के बारे में क्या पास है और हमें यह भी पता चलता है कि यदि हम मूल्य से गुजरते हैं तो हम वस्तुओं को कैसे बदल सकते हैं।

जैसा कि मैंने संदर्भ भाषाओं के पास से देखा, मुझे बहुत अधिक नहीं मिला और मैंने पर्ल का चयन किया। आइए देखें कि प्रतिलिपि पर्ल में कैसे काम करती है:

मेरा $ x = 'स्ट्रिंग';
मेरी $ y = $ x;
$ x = 'नया स्ट्रिंग';
प्रिंट "$ x"; => 'नया तार'
प्रिंट "$ y"; => 'स्ट्रिंग'
मेरे $ a = {data => "string"};
मेरे $ b = $ a;
$ a -> {data} = "new string";
"$ a -> {डेटा} \ n" प्रिंट करें; => 'नया तार'
"$ b -> {डेटा} \ n" प्रिंट करें; => 'नया तार'

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

अब आइए देखें कि संदर्भ से क्या गुजरता है:

मेरा $ x = 'स्ट्रिंग';
प्रिंट "$ x"; => 'स्ट्रिंग'
उप फू {
  $ _ [0] = 'नई स्ट्रिंग';
  प्रिंट "$ _ [0]"; => 'नया तार'
}
foo ($ x);
प्रिंट "$ x"; => 'नया तार'

चूँकि Perl को संदर्भ द्वारा पारित किया जाता है यदि आप फ़ंक्शन के भीतर पुनर्मूल्यांकन करते हैं तो यह मेमोरी एड्रेस के मूल मान को भी बदल देगा।

मूल्य भाषा से पास होने के लिए मैंने गो को चुना है क्योंकि मैं अपने भविष्य के बारे में जानने के लिए अपने भविष्य के बारे में गहराई से जानना चाहता हूं:

पैकेज मुख्य
आयात "fmt"
func changeAddress (एक * int) {
  fmt.Println (क)
  * a = 0 // मेमोरी एड्रेस की वैल्यू को 0 पर सेट करना
}
func changeValue (एक इंट) {
  fmt.Println (क)
  a = 0 // हम फ़ंक्शन के भीतर मान बदलते हैं
  fmt.Println (क)
}
दुर्गंध मुख्य () {
  a: = ५
  fmt.Println (क)
  fmt.Println (और एक)
  changeValue (a) // a को मान से पास किया जाता है
  fmt.Println (क)
  changeAddress (& a) // a का मेमोरी एड्रेस वैल्यू द्वारा पास किया जाता है
  fmt.Println (क)
}
जब आप कोड संकलित और चलाएंगे तो आपको निम्नलिखित मिलेंगे:
0xc42000e328
5
5
0
5
0xc42000e328
0

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

& ऑपरेटर अपने ऑपरेंड को एक पॉइंटर बनाता है और * ऑपरेटर पॉइंटर के अंतर्निहित मूल्य को दर्शाता है। इसका मूल रूप से मतलब है कि आप किसी मान के मेमोरी एड्रेस को पास कर देते हैं और आप मेमोरी एड्रेस के मान को * के साथ सेट करते हैं।

निष्कर्ष

किसी भाषा का मूल्यांकन कैसे करें:

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

हम यहाँ जो भाषाएँ इस्तेमाल करते हैं:

  • जाओ: नकल और मूल्य से पारित कर दिया
  • जावास्क्रिप्ट: आदिम प्रकारों को मूल्य द्वारा कॉपी / पास किया जाता है, वस्तुओं को संदर्भ मूल्य द्वारा कॉपी / पास किया जाता है
  • रूबी: संदर्भ मूल्य + परस्पर / अपरिवर्तनीय वस्तुओं द्वारा कॉपी और पास किया गया
  • पर्ल: संदर्भ मूल्य द्वारा कॉपी किया गया और संदर्भ द्वारा पारित किया गया

जब लोग कहते हैं कि वे संदर्भ से गुजरते हैं तो उनका मतलब आमतौर पर संदर्भ मूल्य से होता है। रेफरेंस वैल्यू द्वारा पास होने का मतलब है कि वैरिएबल को वैल्यू के आसपास पास किया गया है, लेकिन वे वैल्यू ऑब्जेक्ट्स के संदर्भ हैं।

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

ज्यादातर मुख्यधारा की भाषाएं या तो कॉपी की जाती हैं और मूल्य द्वारा पारित की जाती हैं या संदर्भ मूल्य द्वारा कॉपी और पारित की जाती हैं। अंतिम समय के लिए: संदर्भ मूल्य द्वारा पास को आमतौर पर संदर्भ द्वारा पास कहा जाता है।

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

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

संदर्भ या संदर्भ मान से पास आमतौर पर जावास्क्रिप्ट, रूबी और पायथन जैसी उच्च स्तरीय भाषाओं द्वारा उपयोग किया जाता है।

जब आप एक नई भाषा की खोज करते हैं तो इस प्रक्रिया से गुजरते हैं जैसे हमने यहां किया था और आप समझेंगे कि यह कैसे काम करती है।

यह एक आसान विषय नहीं है और मुझे यकीन नहीं है कि जो कुछ मैंने यहां लिखा है वह सब कुछ सही है। अगर आपको लगता है कि मैंने इस लेख में कुछ गलतियाँ की हैं, तो कृपया मुझे टिप्पणियों में बताएं।