लिस्प प्रोग्रामर्स का दावा है कि लिस्प एक शक्तिशाली भाषा है, जिसे आदिम परिचालनों के बहुत छोटे समूह से बनाया जा सकता है । आइए उस विचार को एक बोली के लिए दुभाषिया को गोल्फ द्वारा अभ्यास में लाएं tinylisp
।
भाषा विनिर्देशन
इस विनिर्देशन में, किसी भी स्थिति जिसका परिणाम "अपरिभाषित" के रूप में वर्णित है, वह आपके दुभाषिया में कुछ भी कर सकता है: दुर्घटना, चुपचाप विफल, यादृच्छिक gobbldegook का उत्पादन, या अपेक्षा के अनुसार काम करें। पायथन 3 में एक संदर्भ कार्यान्वयन उपलब्ध है यहां है ।
वाक्य - विन्यास
Tinylisp में टोकन हैं (
, )
या कोष्ठक या अंतरिक्ष को छोड़कर एक या अधिक प्रिंट योग्य ASCII वर्ण के किसी भी स्ट्रिंग। (यानी निम्नलिखित regex:। [()]|[^() ]+
) कोई भी टोकन जिसमें पूरी तरह से अंक होते हैं , पूर्णांक शाब्दिक होता है। (अग्रणी शून्य ठीक हैं।) किसी भी टोकन है कि गैर अंक हैं, एक प्रतीक है भी सांख्यिक दिखने उदाहरण की तरह है 123abc
, 3.14
और -10
। सभी व्हाट्सएप (एक न्यूनतम, एएससीआईआई अक्षर 32 और 10 में) को नजरअंदाज कर दिया जाता है, इंसोफर को छोड़कर क्योंकि यह टोकन को अलग करता है।
एक टिनिइलिस प्रोग्राम में अभिव्यक्तियों की एक श्रृंखला होती है। प्रत्येक अभिव्यक्ति या तो एक पूर्णांक, एक प्रतीक, या एक एस-अभिव्यक्ति (सूची) है। कोष्ठक में लिपटे शून्य या अधिक अभिव्यक्तियों से संबंधित सूची। वस्तुओं के बीच किसी विभाजक का उपयोग नहीं किया जाता है। यहाँ अभिव्यक्ति के उदाहरण हैं:
4
tinylisp!!
()
(c b a)
(q ((1 2)(3 4)))
अभिव्यक्तियाँ जो अच्छी तरह से गठित नहीं होती हैं (विशेष रूप से, जिसमें बेजोड़ कोष्ठक होते हैं) अपरिभाषित व्यवहार देती हैं। (संदर्भ कार्यान्वयन ऑटो खुले हुए पार्न्स को बंद कर देता है और बेजोड़ करीबी पार्न्स पर पार्स करना बंद कर देता है।)
जानकारी का प्रकार
टिनिस्प के डेटा प्रकार पूर्णांक, प्रतीक और सूची हैं। अंतर्निहित कार्यों और मैक्रोज़ को भी एक प्रकार माना जा सकता है, हालांकि उनका आउटपुट स्वरूप अपरिभाषित है। एक सूची में किसी भी प्रकार के मूल्यों की संख्या हो सकती है और मनमाने ढंग से गहराई से घोंसला बनाया जा सकता है। पूर्णांक को कम से कम -2 ^ 31 से 2 ^ 31-1 तक समर्थित होना चाहिए।
खाली सूची - ()
नालो को शून्य के रूप में संदर्भित किया जाता है - और पूर्णांक 0
एकमात्र मान हैं जो तार्किक रूप से गलत माना जाता है; अन्य सभी पूर्णांकों, गैर-रिक्त सूचियों, बिल्डिंस और सभी प्रतीकों तार्किक रूप से सत्य हैं।
मूल्यांकन
एक कार्यक्रम में अभिव्यक्तियों का मूल्यांकन क्रम में किया जाता है और प्रत्येक के परिणाम स्टडआउट को भेजे जाते हैं (बाद में आउटपुट स्वरूपण पर अधिक)।
- पूर्णांक शाब्दिक स्वयं का मूल्यांकन करता है।
- खाली सूची
()
स्वयं का मूल्यांकन करती है। - एक या एक से अधिक वस्तुओं की सूची इसके पहले आइटम का मूल्यांकन करती है और इसे फ़ंक्शन या मैक्रो के रूप में मानती है, शेष वस्तुओं को तर्क के रूप में बुलाती है। यदि आइटम कोई फ़ंक्शन / मैक्रो नहीं है, तो व्यवहार अपरिभाषित है।
- एक प्रतीक एक नाम के रूप में मूल्यांकन करता है, जो वर्तमान फ़ंक्शन में उस नाम से बंधा हुआ मान देता है। यदि वर्तमान फ़ंक्शन में नाम को परिभाषित नहीं किया गया है, तो यह वैश्विक दायरे में इसके लिए बाध्य मूल्य का मूल्यांकन करता है। यदि नाम वर्तमान या वैश्विक दायरे में परिभाषित नहीं किया गया है, तो परिणाम अपरिभाषित है (संदर्भ कार्यान्वयन त्रुटि संदेश देता है और शून्य देता है)।
अंतर्निहित कार्यों और मैक्रोज़
टिनिइलिश में सात अंतर्निहित कार्य हैं। एक फ़ंक्शन उनके कुछ कार्यों को लागू करने और परिणाम को वापस करने से पहले इसके प्रत्येक तर्क का मूल्यांकन करता है।
c
- वाणिज्यदूत [सूची सूची]। दो तर्क, एक मूल्य और एक सूची लेता है, और सूची के सामने मूल्य जोड़कर प्राप्त एक नई सूची देता है।h
- सिर ( कार , लिस्प शब्दावली में)। एक सूची लेता है और इसमें पहला आइटम देता है, या शून्य दिए जाने पर शून्य कर देता है।t
- पूंछ ( सीडीआर , लिस्प शब्दावली में)। एक सूची लेता है और एक नई सूची देता है जिसमें सभी आइटम होते हैं, लेकिन पहले आइटम या शून्य दिए जाते हैं।s
- घटाना। दो पूर्णांकों को लेता है और पहले ऋण को दूसरा देता है।l
- से कम। दो पूर्णांक लेता है; 1 रिटर्न अगर पहले दूसरे से कम है, तो 0।e
- बराबरी का। एक ही प्रकार के दो मान लेता है (दोनों पूर्णांक, दोनों सूची, या दोनों प्रतीक); 1 लौटाता है यदि दोनों समान हैं (या प्रत्येक तत्व में समान), 0 अन्यथा। समानता के लिए परीक्षण निर्माण अपरिभाषित है (संदर्भ कार्यान्वयन अपेक्षित रूप से कार्य करता है)।v
- eval। एक सूची, पूर्णांक या प्रतीक लेता है, एक अभिव्यक्ति का प्रतिनिधित्व करता है, और इसका मूल्यांकन करता है। जैसे करना(v (q (c a b)))
वैसा ही करना(c a b)
;(v 1)
देता है1
।
यहां "मान" में किसी भी सूची, पूर्णांक, प्रतीक, या बिलिन शामिल हैं, जब तक कि अन्यथा निर्दिष्ट न हो। यदि किसी फ़ंक्शन को विशिष्ट प्रकारों के रूप में सूचीबद्ध किया जाता है, तो विभिन्न प्रकारों को पास करना अपरिभाषित व्यवहार होता है, जैसा कि गलत संख्या में तर्कों को पारित करना है (संदर्भ कार्यान्वयन आमतौर पर क्रैश होता है)।
टिनिइस्प में तीन अंतर्निहित मैक्रोज़ हैं। एक मैक्रो, एक फ़ंक्शन के विपरीत, उन्हें ऑपरेशन लागू करने से पहले इसके तर्कों का मूल्यांकन नहीं करता है।
q
- बोली। एक अभिव्यक्ति लेता है और इसे बिना शर्त लौटाता है। उदाहरण के लिए, मूल्यांकन(1 2 3)
करने में त्रुटि होती है क्योंकि यह कॉल करने का प्रयास करता है1
एक फ़ंक्शन या मैक्रो के रूप में , लेकिन(q (1 2 3))
सूची को लौटाता है(1 2 3)
। मूल्यांकनa
करने से नाम को बाध्य मान मिलता हैa
, लेकिन(q a)
नाम ही देता है।i
- अगर। तीन अभिव्यक्ति लेता है: एक शर्त, एक iftrue अभिव्यक्ति, और एक iffalse अभिव्यक्ति। पहले स्थिति का मूल्यांकन करता है। यदि परिणाम गलत है (0
या शून्य), मूल्यांकन करता है और iffalse अभिव्यक्ति देता है। अन्यथा, iftrue अभिव्यक्ति का मूल्यांकन और रिटर्न करता है। ध्यान दें कि जो अभिव्यक्ति वापस नहीं हुई है, उसका मूल्यांकन कभी नहीं किया जाता है।d
- हार। एक प्रतीक और एक अभिव्यक्ति लेता है। अभिव्यक्ति का मूल्यांकन करता है और उसे वैश्विक स्तर पर एक नाम के रूप में दिए गए प्रतीक को बांधता है , फिर प्रतीक लौटाता है। एक नाम को फिर से परिभाषित करने की कोशिश करना चाहिए (चुपचाप, संदेश के साथ, या दुर्घटनाग्रस्त होने से; संदर्भ कार्यान्वयन एक त्रुटि संदेश प्रदर्शित करता है)। नोट: नाम को पास करने से पहले उसे उद्धृत करना आवश्यक नहीं हैd
, हालांकि अभिव्यक्ति को उद्धृत करना आवश्यक है यदि यह एक सूची या प्रतीक है जिसका आप मूल्यांकन नहीं करना चाहते हैं: जैसे(d x (q (1 2 3)))
,।
एक मैक्रो को तर्कों की गलत संख्या पास करना अपरिभाषित व्यवहार (संदर्भ कार्यान्वयन क्रैश) है। ऐसा कुछ पास करना जो एक प्रतीक नहीं है क्योंकि इसका पहला तर्क d
अपरिभाषित व्यवहार है (संदर्भ कार्यान्वयन त्रुटि नहीं देता है, लेकिन मूल्य बाद में संदर्भित नहीं किया जा सकता है)।
उपयोगकर्ता-परिभाषित कार्य और मैक्रोज़
इन दस बिल्ट-इन से शुरू करके, नए कार्यों और मैक्रोज़ का निर्माण करके भाषा को बढ़ाया जा सकता है। इनका कोई समर्पित डेटा प्रकार नहीं है; वे एक निश्चित संरचना के साथ बस सूचियाँ हैं:
- एक फ़ंक्शन दो मदों की एक सूची है। पहला या तो एक या अधिक पैरामीटर नामों की एक सूची है, या एक एकल नाम जो फ़ंक्शन को दिए गए किसी भी तर्क की एक सूची प्राप्त करेगा (इस प्रकार चर-अर्गिटी कार्यों के लिए अनुमति देता है)। दूसरा एक अभिव्यक्ति है जो फ़ंक्शन बॉडी है।
- एक मैक्रो एक फ़ंक्शन के समान है, सिवाय इसके कि इसमें पैरामीटर नाम (ओं) से पहले शून्य होता है, इस प्रकार यह तीन-आइटम सूची बनाता है। (तीन आइटम सूचियों को कॉल करने की कोशिश करना जो शून्य से शुरू नहीं होता है अपरिभाषित व्यवहार है; संदर्भ कार्यान्वयन पहले तर्क की उपेक्षा करता है और उन्हें मैक्रोज़ भी मानता है।)
उदाहरण के लिए, निम्न अभिव्यक्ति एक फ़ंक्शन है जो दो पूर्णांक जोड़ता है:
(q List must be quoted to prevent evaluation
(
(x y) Parameter names
(s x (s 0 y)) Expression (in infix, x - (0 - y))
)
)
और एक मैक्रो जो किसी भी संख्या में तर्क लेता है और पहले का मूल्यांकन करता है और वापस करता है:
(q
(
()
args
(v (h args))
)
)
फ़ंक्शंस और मैक्रोज़ को सीधे कहा जा सकता है, उपयोग करने वाले नामों के लिए बाध्य है d
, और अन्य कार्यों या मैक्रोज़ को पारित किया गया है।
चूंकि फ़ंक्शन बॉडी को परिभाषा समय पर निष्पादित नहीं किया जाता है, इसलिए पुनरावर्ती कार्य आसानी से निश्चित हैं:
(d len
(q (
(list)
(i list If list is nonempty
(s 1 (s 0 (len (t list)))) 1 - (0 - len(tail(list)))
0 else 0
)
))
)
ध्यान दें, हालांकि, ऊपर एक लंबाई फ़ंक्शन को परिभाषित करने का एक अच्छा तरीका नहीं है क्योंकि यह उपयोग नहीं करता है ...
टेल-कॉल पुनरावृत्ति
लिस्प में टेल-कॉल पुनरावृत्ति एक महत्वपूर्ण अवधारणा है। यह कुछ प्रकार के पुनरावृत्ति को छोरों के रूप में लागू करता है, इस प्रकार कॉल स्टैक को छोटा रखता है। आपके टिनिइलिस्प दुभाषिया को उचित टेल-कॉल पुनरावर्तन लागू करना चाहिए !
- यदि उपयोगकर्ता-परिभाषित फ़ंक्शन या मैक्रो की वापसी अभिव्यक्ति किसी अन्य उपयोगकर्ता-परिभाषित फ़ंक्शन या मैक्रो के लिए एक कॉल है, तो आपके दुभाषिए को उस कॉल का मूल्यांकन करने के लिए पुनरावर्तन का उपयोग नहीं करना चाहिए। इसके बजाय, यह वर्तमान फ़ंक्शन और तर्कों को नए फ़ंक्शन और तर्कों और लूप से बदल देना चाहिए जब तक कि कॉल की श्रृंखला हल न हो जाए।
- यदि उपयोगकर्ता-परिभाषित फ़ंक्शन या मैक्रो की वापसी अभिव्यक्ति कॉल है
i
, तो उस शाखा का तुरंत मूल्यांकन न करें जो चयनित है। इसके बजाय, जांचें कि क्या यह किसी अन्य उपयोगकर्ता-परिभाषित फ़ंक्शन या मैक्रो के लिए कॉल है। यदि हां, तो ऊपर दिए गए फ़ंक्शन और तर्कों को स्वैप करें। यह मनमाने ढंग से गहरी नेस्टेड घटनाओं के लिए लागू होता हैi
।
टेल रिकर्शन को डायरेक्ट रिकर्सन (एक फंक्शन कॉल खुद) और इनडायरेक्ट रिकर्सन (फंक्शन a
कॉल फंक्शन b
जिसे कॉल करता है [आदि] जिसे फंक्शन कहते हैं) दोनों के लिए काम करना चाहिए a
।
एक पूंछ-पुनरावर्ती लंबाई समारोह (एक सहायक कार्य के साथ len*
):
(d len*
(q (
(list accum)
(i list
(len*
(t list)
(s 1 (s 0 accum))
)
accum
)
))
)
(d len
(q (
(list)
(len* list 0)
))
)
यह कार्यान्वयन मनमाने ढंग से बड़ी सूचियों के लिए काम करता है, जो केवल अधिकतम पूर्णांक आकार द्वारा सीमित है।
क्षेत्र
फ़ंक्शन पैरामीटर स्थानीय चर (वास्तव में स्थिरांक हैं, क्योंकि वे संशोधित नहीं किए जा सकते हैं)। वे कार्यक्षेत्र में हैं, जबकि उस फ़ंक्शन के उस कॉल के शरीर को निष्पादित किया जा रहा है, और किसी भी गहरी कॉल के दौरान और फ़ंक्शन के वापस आने के बाद गुंजाइश से बाहर है। वे वैश्विक रूप से परिभाषित नामों को "छाया" कर सकते हैं, जिससे वैश्विक नाम अस्थायी रूप से अनुपलब्ध हो जाएगा। उदाहरण के लिए, निम्नलिखित कोड 5 नहीं, 41 है:
(d x 42)
(d f
(q (
(x)
(s x 1)
))
)
(f 6)
हालाँकि, निम्न कोड 41 देता है, क्योंकि x
कॉल स्तर 1 कॉल स्तर 2 से सुलभ नहीं है:
(d x 42)
(d f
(q (
(x)
(g 15)
))
)
(d g
(q (
(y)
(s x 1)
))
)
(f 6)
किसी भी समय के दायरे में केवल नाम 1) वर्तमान में निष्पादित फ़ंक्शन के स्थानीय नाम, यदि कोई हो, और 2) वैश्विक नाम हैं।
प्रस्तुत करने की आवश्यकताएँ
इनपुट और आउटपुट
आपका दुभाषिया स्टडिन से या स्टडिन या कमांड-लाइन तर्क के माध्यम से निर्दिष्ट फ़ाइल से प्रोग्राम को पढ़ सकता है। प्रत्येक अभिव्यक्ति का मूल्यांकन करने के बाद, उसे उस अनुरेखण के परिणाम को एक अनुगामी न्यूलाइन के साथ जोड़ना चाहिए।
- इंटेगर को आपके कार्यान्वयन की भाषा के सबसे प्राकृतिक प्रतिनिधित्व में आउटपुट होना चाहिए। ऋणात्मक पूर्णांकों का उत्पादन किया जा सकता है, जिसमें प्रमुख ऋण चिन्ह होते हैं।
- प्रतीकों को स्ट्रिंग के रूप में आउटपुट किया जाना चाहिए, जिसमें कोई आसपास के उद्धरण या भाग न हों।
- सूची को सभी वस्तुओं के साथ आउटपुट किया जाना चाहिए जो अलग-अलग हैं और कोष्ठक में लिपटे हुए हैं। कोष्ठकों के अंदर एक स्थान वैकल्पिक है:
(1 2 3)
और( 1 2 3 )
दोनों स्वीकार्य प्रारूप हैं। - निर्मित कार्यों और मैक्रो का आउटपुट अपरिभाषित व्यवहार है। (संदर्भ व्याख्या उन्हें प्रदर्शित करती है
<built-in function>
।)
अन्य
संदर्भ दुभाषिया में एक REPL पर्यावरण और अन्य फ़ाइलों से टिनिइल मॉड्यूल को लोड करने की क्षमता शामिल है; ये सुविधा के लिए प्रदान किए जाते हैं और इस चुनौती के लिए आवश्यक नहीं हैं।
परीक्षण के मामलों
परीक्षण मामलों को कई समूहों में विभाजित किया जाता है ताकि आप अधिक जटिल लोगों के काम करने से पहले सरल का परीक्षण कर सकें। हालाँकि, वे भी ठीक काम करेंगे यदि आप उन सभी को एक फ़ाइल में एक साथ डंप करते हैं। बस इसे चलाने से पहले शीर्षकों और अपेक्षित आउटपुट को निकालना न भूलें।
यदि आपने टेल-कॉल पुनरावृत्ति को ठीक से लागू किया है, तो अंतिम (बहु-भाग) परीक्षण मामला स्टैक ओवरफ्लो पैदा किए बिना वापस आ जाएगा। संदर्भ कार्यान्वयन मेरे लैपटॉप पर लगभग छह सेकंड में गणना करता है।
-1
, फिर भी मैं मूल्य -1 को उत्पन्न कर सकता हूं (s 0 1)
।
F
में उपलब्ध नहीं हैं G
यदि F
कॉल G
(डायनेमिक स्कूपिंग के साथ), लेकिन वे फ़ंक्शन में भी उपलब्ध नहीं हैं H
यदि H
कोई नेस्टेड फ़ंक्शन अंदर F
(लेक्सिकल स्कूपिंग के साथ) के रूप में परिभाषित किया गया है - परीक्षण केस देखें 5. तो इसे "लेक्सिकल “भ्रामक हो सकता है।