सी

11

पर कार्यात्मक भाषाओं को संकलित करना मान लीजिए कि आप पोर्टेबल सी के लिए एक कार्यात्मक भाषा संकलित कर रहे हैं, और यह भी मान लें कि विभिन्न कारणों से आप रूढ़िवादी कचरा संग्रह के बजाय सटीक चाहते हैं। कचरा कलेक्टर के लिए कोई पोर्टेबल तरीका नहीं है (शायद सामान्य मामले में कोई रास्ता नहीं) यह पता लगाने के लिए कि सी स्टैक पर पॉइंटर क्या है और नहीं है। ऐसा लगता है कि इस समस्या के दो समाधान हैं:सी

  1. छाया स्टैक। प्रत्येक सी फ़ंक्शन को पॉइंटर के बारे में बहीखाता रखने की जानकारी बनाए रखें। यह दृष्टिकोण द्वारा अनुशंसित दृष्टिकोण है LLVM।

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

मुझे ऐसा लगता है कि, यदि आप एक शुद्ध कार्यात्मक भाषा जहां दूसरा दृष्टिकोण लागू होता है के साथ काम कर रहे हैं, यह हस्तलिखित सी

साथ मिश्रण करने के लिए आसान पहला दृष्टिकोण से अधिक कुशल है, साथ ही होना चाहिए

क्या कोई समस्या है जो मैं देख रहा हूं? इस तकनीक के मौजूदा चर्चा या कार्यान्वयन के लिए कोई संदर्भ?

+1

संभवतः सहायक नहीं है, लेकिन मैंने अपनी योजना दुभाषिया के लिए मार्क-स्वीप लिखने के दौरान पहली बार कोशिश की। प्रदर्शन चूस गया, इसलिए मैं सी रनटाइम के ढेर के बाहर पूरी तरह वर्चुअल स्टैक के साथ समाप्त हुआ, मुख्य रूप से क्रॉस-रनटाइम स्टैक आत्मनिरीक्षण लगभग असंभव है। प्रदर्शन भी चूस गया लेकिन gdb/ddd के बिना डीबग करना आसान था। मैंने ऐसा करने का फैसला किया क्योंकि यह दुभाषिया था और जब मैं कार्यान्वयन के कंपाइलर चरण (जो आम तौर पर कभी खत्म नहीं हुआ) तक पहुंच गया। – Deleted

+0

आप वर्तमान ऑपरेशन को पुनरारंभ करने की योजना कैसे बनाते हैं? समय-समय पर चेकपॉइंट सहेजें, फिर अंतिम अच्छा एक (कैसे?) –

+1

@ n.m को पुनर्स्थापित करें: उस संबंध में प्रश्न का महत्वपूर्ण हिस्सा "कोड का कोई दुष्प्रभाव नहीं है"। प्रश्नकर्ता एक शुद्ध कार्यात्मक भाषा मान रहा है, इसलिए कोई भी राज्य कभी संशोधित नहीं होता है। चेकपॉइंट "लेने" की कोई ज़रूरत नहीं है, और जब आप किसी पिछले राज्य में जाते हैं तो आपको किसी भी बदलाव को "पूर्ववत" करने की आवश्यकता नहीं होती है क्योंकि भाषा परिवर्तन करने में सक्षम नहीं है। सिद्धांत रूप में, कोड में आपकी स्थिति आपको प्रोग्राम की स्थिति के बारे में जानने के लिए आवश्यक सबकुछ बताती है। –

उत्तर

1

मुझे लगता है कि आपने जो स्पष्ट बात नहीं की है, वह है कि लगातार बाहर की स्मृति को कैसे संभालना है। जैसा कि आपने इसका वर्णन किया है, मान लें कि मेरे पास एक ऐसा ऑपरेशन है जो उपलब्ध होने से अधिक स्मृति का उपयोग करता है। आखिर में मैं पहुंच गया:

  1. ऑपरेशन के जो भी चरण शुरू करें, हमें सीमा से अधिक ले जाता है।
  2. थोड़ी देर के लिए दौड़ें।
  3. स्मृति से बाहर निकलें।
  4. इस चरण द्वारा आवंटित सभी स्मृति मुक्त (मुफ्त में कुछ और नहीं है)।
  5. को 1.

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

4

यह एक डेटा संरचना का उपयोग कर एक शुद्ध एफपी भाषा डिजाइन करने के लिए संभव है:

typedef enum record_type { RT_SYMBOL, RT_NUMBER, RT_PAIR }; 

struct record 
{ 
    record_type type; 
    void *value; 
}; 

कार्यक्रम और डेटा का उपयोग कर records की pairs दर्शाया जा सकता है:

struct pair 
{ 
    record *car; 
    record *cdr; 
}; 

यहाँ है कैसे एक सरल अभिव्यक्ति - 2 * 3 - records का उपयोग करके प्रदर्शित किया जा सकता है:

record r1; 
r1.type = RT_NUMBER; 
r1.value = &two; 

record r2; 
r1.type = RT_NUMBER; 
r1.value = &three; 

record opr1; 
opr1.type = RT_NUMBER; 
opr1.value = &OP_MULT; /* A machine op-code for multiplication. */ 

pair p_oprnds; 
p_oprnds.car = &r1; 
p_oprnds.cdr = &r2; 

pair p; 
p.car = opr1; 
p.cdr = p_oprnds; 

यह लिस्प अभिव्यक्ति के समान है: (* 2 3)। अब आप pairs पर चलने वाली मशीन को परिभाषित कर सकते हैं, car को ऑपरेटर के रूप में और cdr को ऑपरेंड के रूप में देखते हुए। चूंकि हम केवल एक डेटा संरचना से निपटते हैं, सटीक जीसी संभव है। ऐसे वीएम की वास्तुकला के लिए Lispkit Lisp देखें।

एफपी -> सी कंपाइलर लिखने के गंभीर प्रयास के साथ शुरू करने से पहले Lisp in Small Pieces भी पढ़ें।