6

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

(defun test() 
    (setf original '(a b c)) 
    (modify original) 
    (print original)) 
(defun modify (n) 
    (setf n '(x y z)) 
    n)

आप (परीक्षण) कॉल करते हैं, यह प्रिंट (एक ख ग) भले ही (संशोधित) रिटर्न (X Y Z)।

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

(defun test() 
    (setf original '(a b c)) 
    (modify original) 
    (print original)) 
(defun modify (n) 
    (setf (first n) 'x) 
    n)

फिर (परीक्षण) प्रिंट (x b c)। तो मैं फ़ंक्शन में सूची पैरामीटर के कुछ तत्व कैसे बदलूं, जैसे कि उस सूची में वह सूची स्थानीय थी?

+3

ध्यान दें कि शाब्दिक स्थिरांक को संशोधित करने के परिणामों अपरिभाषित है। ऐसा मत करो। कभी नहीँ। '(ए बी सी) आपके कोड में एक शाब्दिक स्थिर है। आपको इसे संशोधित नहीं करना चाहिए। आप सूची सूची के साथ बनाए गए सूचियों को संशोधित कर सकते हैं (सूची 'ए' बी 'सी)। –

+4

यह भी ध्यान दें कि (एसईटीएफ मूल '(ए बी सी)) कोई समझ नहीं आता है। एसईटीएफ वैरिएबल पेश नहीं करता है।चर 'मूल' कहीं भी परिभाषित नहीं है। आप LET, DEFUN, DEFVAR, DEFPARAMETER के माध्यम से पेश किए गए चर सेट कर सकते हैं ... –

उत्तर

7

SETF एक जगह संशोधित करता है। n एक जगह हो सकता है। सूची के पहले तत्व n अंक भी एक हो सकता है जगह।

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

एसईटीएफ के बाद पहले मामले में n संशोधित करने के बाद, यह अब उस सूची को इंगित करता है लेकिन एक नई सूची में नहीं आता है। original द्वारा इंगित सूची अप्रभावित है। नई सूची को modify द्वारा वापस कर दिया जाता है, लेकिन चूंकि यह मान किसी भी चीज़ को असाइन नहीं किया जाता है, इसलिए यह अस्तित्व से बाहर हो जाता है और जल्द ही कचरा इकट्ठा किया जाएगा।

दूसरे मामले में, एसईटीएफ n संशोधित नहीं करता है, लेकिन n सूची के पहले तत्व को संशोधित करता है। यह वही सूची original अंक है, इसलिए, बाद में, आप इस चर के माध्यम से संशोधित सूची भी देख सकते हैं।

सूची कॉपी करने के लिए, COPY-LIST का उपयोग करें।

2

आपको शायद समस्याएं हैं क्योंकि लिस्पे ऑब्जेक्ट्स के पास-बाय-वैल्यू संदर्भ पास किए गए हैं, जैसे जावा या पायथन में। आपके विपक्ष कक्षों में वे संदर्भ होते हैं जिन्हें आप संशोधित करते हैं, ताकि आप मूल और स्थानीय दोनों को संशोधित कर सकें।

आईएमओ, आपको ऐसी समस्याओं से बचने के लिए एक और अधिक कार्यात्मक शैली में कार्यों को लिखने का प्रयास करना चाहिए। हालांकि आम लिस्प बहु-प्रतिमान है, एक कार्यात्मक शैली एक और उचित तरीका है।

(defun संशोधित (एन) (विपक्ष 'एक्स (सीडीआर एन))

+0

यह मेरी पहली परियोजना है lisp में, और मैं अभी तक कार्यात्मक प्रोग्रामिंग को वास्तव में समझ नहीं पा रहा हूं। मुझे लगता है कि मुझे अपने कार्यक्रम को संरचित करने के तरीके पर पुनर्विचार करने की जरूरत है? मैं राज्य को संशोधित किए बिना एक गेम राज्य में संभावित संशोधनों की जांच करने की कोशिश कर रहा हूं। तो बदलाव और अचयनित करने के लिए फ़ंक्शंस बनाने की बजाय, मुझे आशा थी कि मैं इसके बजाय राज्य की "प्रतिलिपि" को संशोधित कर सकता हूं। – Ross

+0

आम तौर पर अनावश्यक राज्य परिवर्तनों से बचने के लिए बुद्धिमानी है। अक्सर आपको उनकी आवश्यकता नहीं होती है और वे आपको इस तरह की गलतियों को डीबग करने और नेतृत्व करने के लिए कठिन परिश्रम करते हैं। – freiksenet

11

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

एक अच्छी किताब Touretzky, Common Lisp: A Gentle Introduction to Symbolic Computation है।

वहाँ भी है सॉफ्टवेयर है कि सूचियों और विपक्ष कोशिकाओं के पेड़ खींचता है।

आप इस प्रकार का कार्य करने के लिए एक सूची पार कर लेते हैं:

(modify (list 1 2 3)) 

तो फिर तुम सूची का उपयोग करने के तीन अलग-अलग तरीके होते हैं: विपक्ष कोशिकाओं के

विनाशकारी संशोधन

(defun modify (list) 
    (setf (first list) 'foo)) ; This sets the CAR of the first cons cell to 'foo . 

संरचना साझा करने

(defun modify (list) 
    (cons 'bar (rest list))) 

ऊपर एक सूची लौटाती है कि सूची में पारित के साथ साझा करता संरचना: बाकी तत्वों दोनों सूचियों में ही हैं।

नकल

(defun modify (list) 
    (cons 'baz (copy-list (rest list)))) 

समारोह से ऊपर बाा बार के समान है, लेकिन कोई सूची कोशिकाओं, साझा कर रहे हैं के बाद से सूची की नकल की है।

जरूरत नहीं कहना है कि विनाशकारी संशोधन अक्सर, बचा जाना चाहिए जब तक कि वहाँ यह (बचत स्मृति की तरह जब यह इसके लायक है) करने के लिए एक असली कारण है।

नोट्स:

कभी नहीं विध्वंस शाब्दिक निरंतर सूचियों

न संशोधित 'कार्य करें: (जाने ((एल' (एबीसी))) (setf (प्रथम एल) 'बार))

कारण: सूची संरक्षित लिखने जा सकता है या अन्य सूचियों (संकलक द्वारा आयोजित) के साथ साझा किया जा सकता है, आदि

इसके अलावा:

इस

(let ((original (list 'a 'b 'c))) 
    (setf (first original) 'bar)) 

की तरह चर

परिचय या इस

(defun foo (original-list) 
    (setf (first original-list) 'bar)) 

की तरह कभी नहीं एक अपरिभाषित चर SETF।

void modify1(char *p) { 
    p = "hi"; 
} 

void modify2(char *p) { 
    p[0] = 'h'; 
} 
दोनों ही मामलों एक सूचक पारित हो जाता है, अगर आप सूचक बदलने के लिए, आप (सूचक मूल्य के पैरामीटर प्रति बदल रहे हैं कि उस पर है में

:

+0

धन्यवाद। यह मेरे लिए बहुत उपयोगी था। – Ross

4

यह सी में इस उदाहरण के रूप में लगभग एक ही है ढेर), यदि आप सामग्री बदलते हैं, तो आप जो वस्तु ऑब्जेक्ट की गई थी उसका मूल्य बदल रहे हैं।

संबंधित मुद्दे

 संबंधित मुद्दे