कृपया लिस्प पर पॉल ग्राहम के कुछ बिंदुओं की व्याख्या करें


146

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

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

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

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

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

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


10
मुझे यकीन नहीं है कि फंक्शनल-प्रोग्रामिंग टैग को यहां वारंट किया गया है, क्योंकि कई लिसप्स में अनिवार्य या OO कोड लिखना समान रूप से संभव है क्योंकि यह फंक्शनल कोड लिखना है - और वास्तव में बहुत गैर-कार्यात्मक लिस्प है चारों ओर कोड। मेरा सुझाव है कि आप fp टैग को हटा दें और इसके बजाय क्लोजर जोड़ें - उम्मीद है कि यह JVM- आधारित लिस्पर्स से कुछ दिलचस्प इनपुट ला सकता है।
मिशैल मार्कज़ी

58
हमारे paul-grahamयहाँ एक टैग भी है !!! महान ...
6

@missingfaktor शायद इसे एक जलाऊ-अनुरोध की
बिल्ली

जवाबों:


98

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

अंक (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। ध्यान दें कि Clojure यकीनन आपकी सूची के चौथे बिंदु को पूरी तरह से संतुष्ट नहीं करता है, क्योंकि रीड-टाइम वास्तव में उपयोगकर्ता कोड के लिए खुला नहीं है; मैं इस बारे में चर्चा करूंगा कि इसके अन्यथा होने का क्या मतलब होगा।

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

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

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

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

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

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

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

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

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

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

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


4
सावधान: "नियमित (संकलक) मैक्रो" कहकर, आप यह अनुमान लगाने के
केन

Ken: अच्छा पकड़, धन्यवाद! मैं इसे "नियमित मैक्रो" में बदल दूंगा, जो मुझे लगता है कि किसी को भी यात्रा करने की संभावना नहीं है।
मिचेल मार्कीज

शानदार जवाब। मैंने 5 मिनट में इससे अधिक सीख लिया, जितना कि प्रश्न पूछने के घंटों में मैंने सोचा है। धन्यवाद।
चार्ली फ्लावर्स

संपादित करें: argh, एक रन-ऑन वाक्य को गलत समझा। व्याकरण के लिए ठीक किया गया (मेरे संपादन को स्वीकार करने के लिए एक "सहकर्मी" की आवश्यकता है)।
तातियाना रचेवा

एस-एक्सप्रेशन और एक्सएमएल एक ही संरचना तय कर सकते हैं लेकिन एक्सएमएल कहीं अधिक क्रिया है और इसलिए सिंटैक्स के रूप में अनुकूल नहीं है।
सिल्वेस्टर

66

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))

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

(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) पूरी भाषा हमेशा उपलब्ध है। रीड-टाइम, संकलन-समय और रनटाइम के बीच कोई वास्तविक अंतर नहीं है। आप पढ़ते समय, कोड पढ़ते या चलाते समय कोड को कंपाइल या रन कर सकते हैं, और रनटाइम पर कोड को पढ़ या कंपाइल कर सकते हैं।

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

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

वे C या Java जैसी भाषाओं में कैसे भिन्न हैं?

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

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

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

अंतर:

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


33

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

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

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

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

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


7
ओह, यह शक्ति (और सरलता) अब 50 वर्ष से अधिक हो गई है, और यह लागू करने के लिए काफी आसान है कि एक नौसिखिया प्रोग्रामर इसे न्यूनतम मार्गदर्शन के साथ धमाका कर सकता है, और भाषा की बुनियादी बातों के बारे में जान सकता है। आप जावा, सी, पायथन, पर्ल, हास्केल इत्यादि के समान दावे को एक अच्छी शुरुआत के प्रोजेक्ट के रूप में नहीं सुनेंगे!
मैट कर्टिस

9
मुझे नहीं लगता है कि Java वैरिएबल लिस्प प्रतीकों की तरह हैं। जावा में एक प्रतीक के लिए कोई संकेतन नहीं है, और केवल एक चीज जो आप एक चर के साथ कर सकते हैं वह है इसके मूल्य सेल। स्ट्रिंग्स को नजरअंदाज किया जा सकता है, लेकिन वे आमतौर पर नाम नहीं हैं, इसलिए यह इस बारे में बात करने के लिए भी समझ में नहीं आता है कि क्या उन्हें उद्धृत किया जा सकता है, मूल्यांकन किया जा सकता है, पारित किया जा सकता है
केन

2
40 साल से अधिक पुराना हो सकता है :), @Ken: मुझे लगता है कि उनका मतलब है कि जावा में 1) गैर-आदिम वैरिएबल रिफेंस द्वारा हैं, जो लिस्प के समान है और 2) जावा में नजरबंद तार लिस्प में प्रतीकों के समान हैं - बेशक, जैसा आपने कहा, आप जावा में इंटर्न स्ट्रिंग्स / कोड को उद्धृत या विकसित नहीं कर सकते हैं, इसलिए वे अभी भी काफी भिन्न हैं।

3
@ डैन - निश्चित नहीं है कि पहला कार्यान्वयन एक साथ किया गया था, लेकिन प्रतीकात्मक गणना पर प्रारंभिक मैकार्थी पेपर 1960 में प्रकाशित किया गया था।
इनामाथी

जावा के पास Foo.class / foo.getClass () के रूप में "प्रतीकों" के लिए आंशिक / अनियमित समर्थन है - (यानी एक प्रकार-ए-प्रकार वर्ग <Foo> ऑब्जेक्ट एक सा है - जैसा कि एनम मान हैं,) एक उपाधि। लेकिन एक लिस्प प्रतीक का बहुत न्यूनतम छाया।
BRPocock

-3

अंक (1) और (2) भी पायथन में फिट होंगे। एक सरल उदाहरण "a = str (82.4)" लेते हुए, दुभाषिया पहले मूल्य 82.4 के साथ एक फ्लोटिंग पॉइंट ऑब्जेक्ट बनाता है। फिर यह एक स्ट्रिंग कंस्ट्रक्टर को कॉल करता है, जो 'मान .82' के साथ एक स्ट्रिंग देता है। बाएं हाथ की तरफ वाला 'a' उस स्ट्रिंग ऑब्जेक्ट के लिए एक लेबल मात्र है। मूल फ़्लोटिंग पॉइंट ऑब्जेक्ट कचरा एकत्र किया गया था क्योंकि इसके लिए कोई संदर्भ नहीं हैं।

स्कीम में हर चीज को एक समान तरीके से एक वस्तु के रूप में माना जाता है। मुझे कॉमन लिस्प के बारे में निश्चित नहीं है। मैं C / C ++ अवधारणाओं के संदर्भ में सोचने से बचने की कोशिश करूंगा। जब मैंने लिसप्स की सुंदर सादगी के आसपास अपना सिर पाने की कोशिश कर रहे थे, तब उन्होंने मुझे ढेर कर दिया।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.