lisp meaning




कृपया लिस्प पर पॉल ग्राहम के कुछ बिंदुओं को समझाएं (3)

1) चर की एक नई अवधारणा। लिस्प में, सभी चर प्रभावी ढंग से पॉइंटर्स हैं। मान हैं कि किस प्रकार हैं, वेरिएबल नहीं हैं, और वैरिएबल को असाइन करना या बाध्य करना मतलब है पॉइंटर्स की प्रतिलिपि बनाना, न कि वे क्या इंगित करते हैं।

(defun print-twice (it)
  (print it)
  (print it))

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

डेटा ऑब्जेक्ट्स में 'टाइप' होता है और सभी डेटा ऑब्जेक्ट्स को 'टाइप' के लिए पूछताछ की जा सकती है।

(type-of "abc")  -> STRING

2) एक प्रतीक प्रकार। प्रतीक स्ट्रिंग से भिन्न होते हैं जिसमें आप एक सूचक की तुलना करके समानता का परीक्षण कर सकते हैं।

प्रतीक एक नाम के साथ एक डेटा वस्तु है। आमतौर पर ऑब्जेक्ट को ढूंढने के लिए नाम का उपयोग किया जा सकता है:

|This is a Symbol|
this-is-also-a-symbol

(find-symbol "SIN")   ->  SIN

चूंकि प्रतीक वास्तविक डेटा ऑब्जेक्ट्स हैं, इसलिए हम जांच कर सकते हैं कि वे एक ही ऑब्जेक्ट हैं:

(eq 'sin 'cos) -> NIL
(eq 'sin 'sin) -> T

यह हमें प्रतीकों के साथ एक वाक्य लिखने के लिए उदाहरण के लिए अनुमति देता है:

(defvar *sentence* '(mary called tom to tell him the price of the book))

अब हम वाक्य में संख्या की गणना कर सकते हैं:

(count 'the *sentence*) ->  2

सामान्य लिस्प प्रतीकों में न केवल एक नाम होता है, लेकिन उनके पास एक मूल्य, एक कार्य, एक संपत्ति सूची और एक पैकेज भी हो सकता है। इसलिए चर या चर नाम देने के लिए प्रतीकों का उपयोग किया जा सकता है। संपत्ति सूची आमतौर पर प्रतीकों के लिए मेटा-डेटा जोड़ने के लिए उपयोग की जाती है।

3) प्रतीकों के पेड़ों का उपयोग कर कोड के लिए एक संकेत।

लिस्प कोड का प्रतिनिधित्व करने के लिए अपने मूल डेटा संरचनाओं का उपयोग करता है।

सूची (* 3 2) डेटा और कोड दोनों हो सकती है:

(eval '(* 3 (+ 2 5))) -> 21

(length '(* 3 (+ 2 5))) -> 3

पेड़:

CL-USER 8 > (sdraw '(* 3 (+ 2 5)))

[*|*]--->[*|*]--->[*|*]--->NIL
 |        |        |
 v        v        v
 *        3       [*|*]--->[*|*]--->[*|*]--->NIL
                   |        |        |
                   v        v        v
                   +        2        5

4) पूरी भाषा हमेशा उपलब्ध है। रीड-टाइम, संकलन-समय और रनटाइम के बीच कोई वास्तविक भेद नहीं है। संकलन करते समय कोड पढ़ने, पढ़ने या चलाने के दौरान कोड को संकलित या चला सकते हैं, और रनटाइम पर कोड को पढ़ या संकलित कर सकते हैं।

लिस्प टेक्स्ट को डेटा से कोड और कोड पढ़ने के लिए पढ़ता है, कोड लोड करने के लिए लोड, कोड का मूल्यांकन करने के लिए EVAL, डेटा को संकलित करने के लिए COMPILE और पाठ को डेटा और कोड लिखने के लिए प्रिंट करें।

ये कार्य हमेशा उपलब्ध हैं। वे दूर नहीं जाते हैं। वे किसी भी कार्यक्रम का हिस्सा हो सकते हैं। इसका मतलब है कि कोई प्रोग्राम कोड को पढ़, लोड, eval या प्रिंट कर सकते हैं - हमेशा।

सी या जावा जैसी भाषाओं में वे अलग कैसे हैं?

वे भाषा कोड के रूप में डेटा के डेटा या रनटाइम मूल्यांकन के रूप में प्रतीक, कोड प्रदान नहीं करते हैं। सी में डेटा ऑब्जेक्ट्स आमतौर पर untyped हैं।

क्या एलआईएसपी परिवार भाषाओं के अलावा अन्य कोई भी भाषा अब इनमें से कोई भी संरचना है?

कई भाषाओं में इनमें से कुछ क्षमताएं हैं।

अंतर:

लिस्प में इन क्षमताओं को भाषा में डिजाइन किया गया है ताकि वे उपयोग में आसान हो सकें।

मुझे पॉल ग्राहम के व्हाट मेड लिस्प अलग से कुछ बिंदुओं को समझने में कुछ मदद चाहिए।

  1. चर की एक नई अवधारणा। लिस्प में, सभी चर प्रभावी ढंग से पॉइंटर्स हैं। मान हैं कि किस प्रकार हैं, वेरिएबल नहीं हैं, और वैरिएबल को असाइन करना या बाध्य करना मतलब है पॉइंटर्स की प्रतिलिपि बनाना, न कि वे क्या इंगित करते हैं।

  2. एक प्रतीक प्रकार। प्रतीक स्ट्रिंग से भिन्न होते हैं जिसमें आप एक सूचक की तुलना करके समानता का परीक्षण कर सकते हैं।

  3. प्रतीकों के पेड़ का उपयोग कर कोड के लिए एक संकेत।

  4. पूरी भाषा हमेशा उपलब्ध है। रीड-टाइम, संकलन-समय और रनटाइम के बीच कोई वास्तविक भेद नहीं है। संकलन करते समय कोड पढ़ने, पढ़ने या चलाने के दौरान कोड को संकलित या चला सकते हैं, और रनटाइम पर कोड को पढ़ या संकलित कर सकते हैं।

इन बिंदुओं का क्या अर्थ है? सी या जावा जैसी भाषाओं में वे अलग कैसे हैं? क्या लिस्प परिवार की भाषाओं के अलावा कोई अन्य भाषाएं अब इनमें से कोई भी संरचना है?


अंक (1) और (2) के लिए, वह ऐतिहासिक रूप से बात कर रहा है। जावा के चर काफी समान हैं, यही कारण है कि आपको मानों की तुलना करने के लिए .equals () को कॉल करने की आवश्यकता है।

(3) एस-एक्सप्रेशन के बारे में बात कर रहा है। लिस्प प्रोग्राम इस वाक्यविन्यास में लिखे गए हैं, जो जावा और सी जैसे विज्ञापन-प्रसार वाक्यविन्यास पर बहुत से फायदे प्रदान करते हैं, जैसे सी मैक्रोज़ या सी ++ टेम्पलेट्स से दूर क्लीनर तरीके से मैक्रोज़ में दोहराए गए पैटर्न को कैप्चर करना, और उसी कोर सूची के साथ कोड में हेरफेर करना ऑपरेशन जो आप डेटा के लिए उपयोग करते हैं।

(4) उदाहरण के लिए सी लेना: भाषा वास्तव में दो अलग-अलग उप भाषाएं हैं: जैसे सामान () और जबकि (), और प्रीप्रोसेसर। आप प्रीप्रोसेसर का उपयोग हर समय खुद को दोहराने के लिए सहेजने के लिए करते हैं, या # if / # ifdef के साथ कोड छोड़ने के लिए करते हैं। लेकिन दोनों भाषाएं काफी अलग हैं, और आप () को संकलित समय पर () कर सकते हैं जैसे कि आप #if कर सकते हैं।

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

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


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

अंक (3) और (4) पर:

आपकी सूची में अंक (3) और (4) अब सबसे दिलचस्प और अभी भी प्रासंगिक प्रतीत होते हैं।

उन्हें समझने के लिए, प्रोग्रामर द्वारा टाइप किए गए वर्णों की धारा के रूप में - लिस्प कोड के साथ क्या होता है, इसकी एक स्पष्ट तस्वीर होना उपयोगी होता है - निष्पादित होने के तरीके पर। आइए एक ठोस उदाहरण का उपयोग करें:

;; a library import for completeness,
;; we won't concern ourselves with it
(require '[clojure.contrib.string :as str])

;; this is the interesting bit:
(println (str/replace-re #"\d+" "FOO" "a123b4c56"))

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

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

संकलन समय पर पूरी भाषा:

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

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

पढ़ने की समय पर पूरी भाषा:

आइए उस #"\d+" regex शाब्दिक पर वापस जाएं। जैसा ऊपर बताया गया है, संकलन के लिए तैयार किए जा रहे नए कोड के पहले उल्लेख को सुनने से पहले, इसे पढ़ने के समय में वास्तविक संकलित पैटर्न ऑब्जेक्ट में बदल दिया जाता है। यह कैसे होता है?

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

इसलिए, यदि आप एक आम लिस्प प्रोग्रामर हैं और आपको क्लोजर-स्टाइल वेक्टर अक्षर के विचार पसंद हैं, तो आप पाठक को कुछ वर्ण अनुक्रमों के लिए उचित रूप से प्रतिक्रिया करने के लिए एक फ़ंक्शन में प्लग कर सकते हैं - [ या #[ संभवतः - और मिलान में एक वेक्टर शाब्दिक अंत की शुरुआत के रूप में इसका इलाज करें ] । इस तरह के एक समारोह को एक पाठक मैक्रो कहा जाता है और एक नियमित मैक्रो की तरह, यह किसी भी प्रकार के लिस्प कोड को निष्पादित कर सकता है, जिसमें कोड भी शामिल है जिसे पहले पंजीकृत पाठक मैक्रोज़ द्वारा सक्षम फंकी नोटेशन के साथ लिखा गया है। तो आपके लिए पढ़ने के समय पूरी भाषा है।

इसे लपेट रहा है:

असल में, अब तक क्या दिखाया गया है कि कोई भी पढ़ने के समय या संकलन समय पर नियमित लिस्प फ़ंक्शन चला सकता है; एक कदम को यहां से समझने की जरूरत है कि पढ़ने, संकलन या रन टाइम पर पढ़ने और संकलन कैसे संभव है, यह समझना है कि पढ़ना और संकलन स्वयं लिस्प कार्यों द्वारा किया जाता है। आप वर्ण स्ट्रीम से लिस्प डेटा में पढ़ने या संकलित करने और क्रमशः लिस्प कोड निष्पादित करने के लिए किसी भी समय read या eval कॉल कर सकते हैं। वह पूरी भाषा है, हर समय।

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