2009-12-12 14 views
70

धन्यवाद सभी सुंदर उत्तरों के लिए बहुत कुछ! चिह्नित नहीं कर सकते सिर्फ एक के रूप में सहीमानसिक रूप से लिस्प/क्लोजर कोड को कैसे पढ़ा जाए

नोट: पहले से ही एक विकी

मैं कार्यात्मक प्रोग्रामिंग करने के लिए नया हूँ और जब मैं कार्यात्मक प्रोग्रामिंग में सरल काम करता है, उदाहरण के लिए के लिए पढ़ सकते हैं एक संख्या के फैक्टोरियल की गणना, मुझे बड़े कार्यों को पढ़ने में मुश्किल लग रही है। कारण का एक हिस्सा है कि मुझे लगता है कि फ़ंक्शन परिभाषा के भीतर कोड के छोटे ब्लॉक को समझने में असमर्थता और आंशिक रूप से क्योंकि कोड में () से मिलान करना मेरे लिए मुश्किल हो रहा है।

यह अच्छा होगा अगर कोई मुझे कुछ कोड पढ़ने के माध्यम से चल सके और मुझे कुछ कोड देने के बारे में कुछ सुझाव दें।

नोट: अगर मैं इसे 10 मिनट तक देखता हूं तो मैं इस कोड को समझ सकता हूं, लेकिन मुझे संदेह है कि जावा में यह कोड लिखा गया था, तो मुझे 10 मिनट लगेंगे। इसलिए, मुझे लिस्प स्टाइल कोड में सहज महसूस करने लगता है, मुझे इसे तेज करना होगा

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

(defn concat 
    ([] (lazy-seq nil)) 
    ([x] (lazy-seq x)) 
    ([x y] 
    (lazy-seq 
     (let [s (seq x)] 
     (if s 
      (if (chunked-seq? s) 
      (chunk-cons (chunk-first s) (concat (chunk-rest s) y)) 
      (cons (first s) (concat (rest s) y))) 
      y)))) 
    ([x y & zs] 
    (let [cat (fn cat [xys zs] 
       (lazy-seq 
        (let [xys (seq xys)] 
        (if xys 
         (if (chunked-seq? xys) 
         (chunk-cons (chunk-first xys) 
            (cat (chunk-rest xys) zs)) 
         (cons (first xys) (cat (rest xys) zs))) 
         (when zs 
         (cat (first zs) (next zs)))))))] 
     (cat (concat x y) zs)))) 
+3

अनुभव? एक जिसे आप लिस्प कोड पढ़ने के लिए उपयोग करते हैं, यह तेज़ होगा। हालांकि, लिस्प के बारे में मुख्य शिकायतों में से एक यह है कि इसे पढ़ना मुश्किल है, इसलिए उम्मीद न करें कि यह आपके लिए सहज हो जाए। –

+10

यह एक आसान काम नहीं है। यदि आप इसे 10 मिनट के बाद पूरी तरह से समझ सकते हैं, तो आप ठीक हैं। –

उत्तर

47

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

सबसे पहले, कोड पढ़ने पर, कोष्ठक के बारे में चिंता न करें। इंडेंटेशन के बारे में चिंता करें। सामान्य नियम यह है कि एक ही इंडेंट स्तर पर चीजें संबंधित हैं। तो:

 (if (chunked-seq? s) 
     (chunk-cons (chunk-first s) (concat (chunk-rest s) y)) 
     (cons (first s) (concat (rest s) y))) 

दूसरा, यदि आप एक पंक्ति पर सबकुछ फिट नहीं कर सकते हैं, तो अगली पंक्ति एक छोटी राशि को इंडेंट करें। यह है लगभग हमेशा दो रिक्त स्थान:

(defn concat 
    ([] (lazy-seq nil)) ; these two fit 
    ([x] (lazy-seq x)) ; so no wrapping 
    ([x y]    ; but here 
    (lazy-seq   ; (lazy-seq indents two spaces 
     (let [s (seq x)] ; as does (let [s (seq x)] 

तीसरा, एक समारोह के लिए कई तर्क, एक पंक्ति पर फिट नहीं कर सकते, तो दूसरा, तीसरा, आदि पहले नीचे तर्क कोष्टक शुरू कर रहा है लाइन अप। कई मैक्रोज़ में महत्वपूर्ण भागों को पहले प्रकट होने की अनुमति देने के लिए विविधताएं होती हैं।

; fits on one line 
(chunk-cons (chunk-first s) (concat (chunk-rest s) y)) 

; has to wrap: line up (cat ...) underneath first (of (chunk-first xys) 
        (chunk-cons (chunk-first xys) 
           (cat (chunk-rest xys) zs)) 

; if you write a C-for macro, put the first three arguments on one line 
; then the rest indented two spaces 
(c-for (i 0) (< i 100) (add1 i) 
    (side-effects!) 
    (side-effects!) 
    (get-your (side-effects!) here)) 

इन नियमों की मदद से आप कोड के भीतर ब्लॉक लगता है: यदि आप

(chunk-cons (chunk-first s) 

देख कोष्ठकों गिनती मत करो!

(chunk-cons (chunk-first s) 
      (concat (chunk-rest s) y)) 

तुम्हें पता है कि पहली पंक्ति एक पूर्ण अभिव्यक्ति क्योंकि अगली पंक्ति के नीचे इसे इंडेंट नहीं है: अगली पंक्ति की जाँच करें।

यदि आप ऊपर से defn concat देखते हैं, तो आप जानते हैं कि आपके पास तीन ब्लॉक हैं, क्योंकि एक ही स्तर पर तीन चीजें हैं। लेकिन तीसरी पंक्ति के नीचे सबकुछ इसके नीचे इंडेंट किया गया है, इसलिए बाकी तीसरे ब्लॉक से संबंधित हैं।

Here is a style guide for Scheme। मुझे क्लोजर नहीं पता है, लेकिन अधिकांश नियम समान होना चाहिए क्योंकि अन्य लिस्प्स में से कोई भी बहुत भिन्न नहीं है।

+0

आपके दूसरे कोड नमूने में, मैं पांचवीं रेखा के इंडेंटेशन को कैसे नियंत्रित करूं, (एमीक्स में आलसी-सीक्यू? डिफ़ॉल्ट रूप से, यह पिछली पंक्ति पर 'वाई' के साथ गठबंधन है। –

+0

क्षमा करें, मुझे नहीं पता मैं क्लोजर का उपयोग नहीं करता हूं और वह उदाहरण सटीक रूप से योजना में अनुवाद नहीं करता है। आप क्लोजर-इंडेंट-ऑफ़सेट जैसे चर के लिए जांच सकते हैं। उदाहरण के लिए, हास्केल के लिए, मुझे '(हैकेल-इंडेंट-ऑफसेट 2) जोड़ना पड़ा था। मेरे कस्टम-सेट-वेरिएबल्स के लिए। –

+0

'स्रोत' फ़ंक्शन का उल्लेख करने के लिए क्लोजर https://github.com/bbatsov/clojure-style-guide –

7

पहले कि कार्यात्मक कार्यक्रम याद भाव, नहीं बयान के होते हैं होगा। उदाहरण के लिए, (if condition expr1 expr2) बूलियन फाल्लू के परीक्षण के लिए अपनी शर्त के रूप में अपना पहला तर्क लेता है, इसका मूल्यांकन करता है, और यदि यह सत्य साबित होता है तो यह मूल्यांकन करता है और expr1 देता है, अन्यथा मूल्यांकन करता है और expr2 देता है। जब प्रत्येक फॉर्म अभिव्यक्ति देता है तो कुछ सामान्य सिंटैक्स संरचनाएं होती हैं जैसे कि THEN या ELSE कीवर्ड अभी गायब हो सकते हैं। ध्यान दें कि यहां if स्वयं भी एक अभिव्यक्ति का मूल्यांकन करता है।

अब मूल्यांकन के बारे में: क्लोजर (और अन्य लिस्पस) में आप जिन फॉर्मों का सामना करते हैं, वे (f a1 a2 ...) फॉर्म की फ़ंक्शन कॉल हैं, जहां f के सभी तर्क वास्तविक फ़ंक्शन कॉल से पहले मूल्यांकन किए जाते हैं; लेकिन रूप भी मैक्रोज़ या विशेष रूप हो सकते हैं जो इसके कुछ तर्कों (या सभी) का मूल्यांकन नहीं करते हैं।
user=> doseq
java.lang.Exception: Can't take value of a macro: #'clojure.core/doseq
एक मैक्रो

user=> apply
#<core$apply__3243 [email protected]>
एक समारोह: संदेह में हैं, तो प्रलेखन (doc f) से परामर्श या सिर्फ आरईपीएल में जाँच करें।

इन दो नियम:

  • हम भाव, बयान नहीं है
  • एक उप-प्रपत्र की
  • मूल्यांकन हो सकता है या नहीं, कैसे बाहरी रूप से व्यवहार करती

के आधार पर लिस्प के अपने groking को कम करना चाहिए कार्यक्रम, esp। अगर आपके पास दिए गए उदाहरण की तरह उनके अच्छे इंडेंटेशन हैं।

उम्मीद है कि इससे मदद मिलती है।

58

मुझे लगता है कि concat समझने की कोशिश करने के लिए एक बुरा उदाहरण है। यह एक कोर फ़ंक्शन है और यह सामान्य रूप से आपके द्वारा लिखे गए कोड से अधिक निम्न-स्तर है, क्योंकि यह कुशल होने का प्रयास करता है।

ध्यान में रखना एक और बात यह है कि जावा कोड की तुलना में क्लोजर कोड बेहद घना है। थोड़ा क्लोजर कोड बहुत काम करता है। जावा में एक ही कोड 23 लाइन नहीं होगा। यह कई वर्गों और इंटरफेस, एक बहुत सी विधियों, स्थानीय अस्थायी फेंकने वाले चर और अजीब लूपिंग संरचनाओं और आम तौर पर सभी प्रकार के बॉयलरप्लेट की संभावना होगी।

कुछ सामान्य युक्तियां हालांकि ...

  1. कोशिश कोष्ठक समय के सबसे अनदेखी करने के लिए। इसके बजाय इंडेंटेशन का उपयोग करें (जैसे नाथन सैंडर्स सुझाव देते हैं)। जैसे

    (if s 
        (if (chunked-seq? s) 
        (chunk-cons (chunk-first s) (concat (chunk-rest s) y)) 
        (cons (first s) (concat (rest s) y))) 
        y)))) 
    

    जब मैं देखो कि मेरे दिमाग देखता है:

    if foo 
        then if bar 
        then baz 
        else quux 
        else blarf 
    
  2. यदि आप एक कोष्ठक पर अपना कर्सर रखें और अपने पाठ संपादक नहीं मिलान एक वाक्य रचना पर प्रकाश डाला है, मैं सुझाव है कि आप पाते हैं एक नया संपादक

  3. कभी-कभी यह कोड को अंदरूनी पढ़ने में मदद करता है। क्लोजर कोड गहराई से घोंसला होता है।

    (let [xs (range 10)] 
        (reverse (map #(/ % 17) (filter (complement even?) xs)))) 
    

    बुरा: "तो हम 1 से 10 तो हम इंतजार मैं भूल गया था कि मैं क्या बारे में बात कर रहा हूँ के पूरक की छानने की मैपिंग के आदेश को पलट रहे हैं, उससे संख्या के साथ शुरू करते हैं।"

    अच्छा:।। "। ठीक है, तो हम ले जा रहे हैं कुछ xs(complement even?) भी के विपरीत अर्थ है, इसलिए" अजीब "तो हम कुछ संग्रह फ़िल्टर कर रहे हैं तो केवल विषम संख्या छोड़ दिया जाता है तो हम कर रहे हैं उन्हें 17 तक विभाजित करना। फिर हम उनके आदेश को उलट रहे हैं। xs प्रश्न में 1 से 10 हैं, गोचा। "

    कभी-कभी यह स्पष्ट रूप से ऐसा करने में मदद करता है। इंटरमीडिएट परिणाम लें, उन्हें let में फेंक दें और उन्हें एक नाम दें ताकि आप समझ सकें। आरईपीएल इस तरह के आसपास खेलने के लिए बनाया जाता है। मध्यवर्ती परिणामों का निष्पादन करें और देखें कि प्रत्येक चरण आपको क्या देता है।

    (let [xs (range 10) 
         odd? (complement even?) 
         odd-xs (filter odd? xs) 
         odd-xs-over-17 (map #(/ % 17) odd-xs) 
         reversed-xs (reverse odd-xs-over-17)] 
        reversed-xs) 
    

    जल्द ही आप बिना किसी प्रयास के मानसिक रूप से इस तरह की चीज करने में सक्षम होंगे।

  4. (doc) का उदार उपयोग करें। आरईपीएल पर उपलब्ध दस्तावेज रखने की उपयोगिता को अधिक नहीं किया जा सकता है। यदि आप clojure.contrib.repl-utils का उपयोग करते हैं और क्लासपाथ पर अपनी .clj फ़ाइलें रखते हैं, तो आप (source some-function) कर सकते हैं और इसके लिए सभी स्रोत कोड देख सकते हैं। आप (show some-java-class) कर सकते हैं और इसमें सभी विधियों का विवरण देखें। और इसी तरह।

कुछ पढ़ने में सक्षम होने के नाते केवल अनुभव के साथ आता है। किसी अन्य भाषा की तुलना में लिस्प को पढ़ने में कोई मुश्किल नहीं है। ऐसा ही होता है कि अधिकांश भाषाएं सी की तरह दिखती हैं, और अधिकांश प्रोग्रामर अपना अधिकांश समय पढ़ते हैं, इसलिए ऐसा लगता है कि सी सिंटैक्स पढ़ने में आसान है। प्रैक्टिस अभ्यास अभ्यास।

+3

+1 के लिए यहां एक स्टाइल गाइड है, उस जानकारी का थोड़ा सा आना कठिन है यह होना चाहिए। अब मुझे जो कुछ याद आया है उसे देखने के लिए clojure.contrib.repl-utils के बाकी हिस्सों में जा रहा है। – vedang

+3

+1 मध्यवर्ती परिणामों को फेंकने के लिए +1, कोड को और अधिक पठनीय बनाने के लिए (क्लोजर नौसिखिया के लिए) –

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