2009-12-12 4 views
70

모든 아름다운 답변에 대해 감사드립니다. 하나로서 올바른정신적으로 읽는 법 Lisp/Clojure 코드

주 표시 할 수 없습니다 : 나는 함수형 프로그래밍에 새로운 오전

이미 위키 나는 예를 들면위한 함수 프로그래밍의 간단한 기능을 읽을 수있는 반면 숫자의 계승을 계산할 때, 큰 기능을 읽는 것이 어렵다는 것을 알고 있습니다. 이유 중 일부는 함수 정의 내에서 더 작은 코드 블록을 파악할 수 없다는 것과 내적으로 코드에서 ()과 일치시키기가 어려워 졌기 때문이라고 생각합니다.

누군가 코드를 읽으면서 나를 걸을 수 있고 코드를 신속하게 해독 할 수있는 방법에 대한 팁을 줄 수 있다면 좋을 것입니다.

참고 :이 코드를 10 분 동안 보았을 때이 코드를 이해할 수 있지만이 코드가 Java로 작성된 것인지 의심 스럽지만 10 분이 걸립니다. 그래서 나는 Lisp 스타일의 코드가 편안하다고 생각한다. 더 빨리해야한다.

Note : 나는 주관적인 질문이다. 그리고 나는 정당하게 정확한 답을 찾는 것이 아닙니다. 그냥이 코드를 읽기에 대해 이동하는 방법에 대한 의견, 환영과

(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

경험이 있으십니까? 여러분은 Lisp 코드를 읽는 것에 익숙해지면 빠를 것입니다. 그러나 Lisp에 대한 주요 불만 중 하나는 읽기가 어렵다는 점입니다. 따라서 직관적으로 이해하기가 쉽지 않을 것입니다. –

+10

이것은 쉬운 기능이 아닙니다. 10 분 후에 완전히 이해할 수 있다면 괜찮습니다. –

답변

47

정규 구문 때문에 Lisp 코드는 다른 기능 언어보다 더 읽기가 어렵습니다. 워치 시크 (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. 나는 Clojure를 모른다. 그러나 대부분의 규칙은 다른 Lisps 중 어느 것도 많이 변하지 않기 때문에 동일해야한다.

+0

두 번째 코드 샘플에서 Emacs의 5 번째 라인 인 들여 쓰기를 제어하는 ​​방법은 무엇입니까? 기본적으로 이전 줄의 'y'와 정렬됩니다. –

+0

죄송합니다, 몰라요. 예를 들어, 하스켈의 경우, '(haskell-indent-offset 2)'를 추가해야만했다. 내 맞춤 설정 변수에. –

+0

다음은 Clojure의 스타일 가이드입니다. https://github.com/bbatsov/clojure-style-guide –

7

먼저 그 기능 프로그램을 기억 표현식이 아닌 문장으로 구성되어 매우 도움이 될 것입니다. 예를 들어 양식 (if condition expr1 expr2)은 첫 번째 인수를 부울 값을 테스트하기위한 조건으로 사용하고 평가 한 다음 true로 평가되면 expr1을 계산하여 반환하고 그렇지 않으면 expr2를 계산하여 반환합니다. 모든 형식이 표현식을 반환하면 THEN 또는 ELSE 키워드와 같은 일반적인 구문 구문 중 일부가 사라질 수 있습니다. 여기 if 자체가 표현식으로 평가된다는 점에 유의하십시오.

이제 Clojure (및 기타 Lisps)에서 가장 많이 접하는 양식은 (f a1 a2 ...) 형식의 함수 호출이며 f에 대한 모든 인수는 실제 함수 호출 전에 평가됩니다. 양식은 매크로 또는 특정 형식 (또는 전체)의 인수를 평가하지 않는 특수 형식 일 수도 있습니다. 의심하는 경우, 문서 (doc f)를 참조하거나 REPL에서 확인하십시오

user=> apply
#<core$apply__3243 [email protected]>
기능
user=> doseq
java.lang.Exception: Can't take value of a macro: #'clojure.core/doseq
매크로를.

이 두 가지 규칙 : 우리가 표현하지 문을

  • 하위 폼의
  • 평가가 발생할 수 있습니다 또는,

를 작동하는 방법을 외부 형태의 따라하는 것은 리스프의 당신의 groking을 완화해야하지 프로그램, 특히 그들이 당신이 준 예제처럼 좋은 들여 쓰기가 있다면.

희망이 도움이됩니다.

58

나는 concat가 이해하려고하는 것은 나쁜 예라고 생각합니다. 그것은 핵심 기능이며, 코드를 작성하는 것이 일반적으로 코드를 작성하는 것보다 훨씬 더 효율적입니다.

Clojure 코드는 Java 코드에 비해 매우 밀도가 높습니다. 작은 Clojure 코드는 많은 작업을 수행합니다. Java에서 같은 코드는 23 행이 아닐 것입니다. 이는 여러 클래스와 인터페이스, 많은 방법, 많은 임시 임시 변수 및 어색한 루핑 구문 및 일반적으로 모든 종류의 상용구 일 수 있습니다.

하지만 일부 일반 팁 ...

  1. 시도는 대부분의 시간 괄호를 무시합니다. 들여 쓰기를 대신 사용하십시오 (Nathan Sanders가 제시 한 것처럼). 예 : 내가 볼 때

    (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. 간혹 코드를 읽을 때 유용합니다. Clojure 코드는 깊게 중첩되는 경향이 있습니다.

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

    나쁜 : 은 "그래서 우리는 1 그럼 우리는 나는 내가 무슨 말 잊었 대기의 보수의 필터링의 매핑의 순서를 반대로하고 10까지 숫자로 시작합니다."

    좋은 :.. . "좋아, 그럼 우리는 어떤 xs(complement even?)도의 반대를 의미 복용하고, 그래서"이상한 "그래서 우리는 몇 가지 콜렉션을 필터링하고 그렇게 만 홀수 번호가 남아 있습니다 그리고 우리가있어 그들을 모두 17로 나눈다. 그러면 우리는 순서를 뒤집을 것이다. 그리고 문제의 xs은 1에서 10까지이다. "

    때로는이를 명시 적으로 수행하는 것이 도움이됩니다. 중간 결과를 가져다가 let에 던져서 이해하도록 이름을 지정하십시오. REPL은 이와 같이 놀기 위해 만들어졌습니다. 중간 결과를 실행하고 각 단계에서 무엇을 제공하는지 확인하십시오.

    (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)으로 설정하십시오. REPL에서 바로 사용할 수있는 문서의 유용성은 과장 될 수 없습니다. clojure.contrib.repl-utils을 사용하고 클래스 경로에 .clj 파일이 있으면 (source some-function)을 수행하고 이에 대한 모든 소스 코드를 볼 수 있습니다. (show some-java-class)을 수행하고 그 안에있는 모든 메소드에 대한 설명을 참조하십시오. 등등.

빠르게 읽을 수있는 것은 경험이있는 것입니다. Lisp은 다른 어떤 언어보다 읽기가 더 어렵지 않습니다. 대부분의 언어가 C처럼 보일뿐입니다. 대부분의 프로그래머가 대부분의 시간을 읽으므로 C 구문이 읽기 쉽습니다. 연습 연습 연습.

+3

+1 '소스'기능에 대해 언급 했으므로 그 정보가 더 어려워졌습니다. 이제 내가 놓친 것을보기 위해 clojure.contrib.repl-utils의 나머지 부분을 살펴 보자. – vedang

+3

+1은 중간 결과를 let으로 던져서 코드를 훨씬 더 읽기 쉬워졌다. (Clojure 초보자를 위해) –

관련 문제