Lisp 커뮤니티가 함수의 끝에 모든 괄호를 누적하는 것을 선호하는 이유는 무엇입니까?


26

Lisp 커뮤니티가 함수의 끝에 모든 괄호를 누적하는 것을 선호하는 이유는 무엇입니까?

(defn defer-expensive [cheap expensive]
  (if-let [good-enough (force cheap)]
    good-enough
    (force expensive)))

C 나 Java 같은 규칙을 사용하지 않겠습니까?
글쎄, Lisp는 그 언어보다 훨씬 오래되었지만 현대 Lispers에 대해 이야기하고 있습니다.

(defn defer-expensive [cheap expensive]
  (if-let [good-enough (force cheap)]
    good-enough
    (force expensive)
  )
)

참고 : 코드 스 니펫은 "The Clojure의 기쁨"책에서 발췌 한 것입니다.


13
펀치 카드 시절에 80 개의 오른쪽 괄호가있는 여분의 카드로 Lisp 프로그램을 끝내는 것이 일반적 관행이라고 들었습니다. 프로그램의 모든 열린 괄호와 일치 할 수 있는지 확인하십시오. .
Alger

2
와우 좋다. 나는 항상 괄호를 싫어했지만 두 번째 예는 나에게 유쾌 해 보인다. 파이썬만큼 좋지는 않지만 개선되었습니다.
Eric Wilson

5
그들이 할 수 있기 때문에?
Muad'Dib

Clojure의 방언이 들여 쓰기를 코드 구조로 승격시키는 것이 좋을 것입니다. 그것은 괄호로 완전히 사라질 것입니다 (F # 등). 물론, 원하는 경우 괄호는 여전히 합법적입니다.
intrepidis 2016 년

답변:


28

Algol 기반 언어가 자신의 라인에 중괄호를 권장하는 한 가지 이유는 중괄호를 이동하지 않고도 구분 중괄호 사이에 더 많은 라인을 추가하는 것을 권장하기 때문입니다. 즉, 시작하면

if (pred)
{
  printf("yes");
}

중괄호 안에 다른 문장을 추가하고 추가하는 것이 쉽습니다 .

if (pred)
{
  printf("yes");
  ++yes_votes;
}

원래 형태가 있었다

if (pred)
{ printf("yes"); }

그런 다음 두 개의 괄호를 "이동"해야 하지만 내 예는 후자를 더 중요하게 생각합니다. 여기서, 중괄호는 일련의 명령문으로 의도 된 것을 한정하고 있으며 , 대부분 부작용을 위해 호출됩니다.

반대로 Lisp에는 진술이 없습니다. 모든 형태는 표현 이며 일부 희귀 한 경우 (공통 리스프를 생각하는 경우에도) 빈 값을 통해 "값이없는"값으로 선택됩니다 (values). 중첩 식과 달리 식 시퀀스 를 찾는 것이 일반적이지 않습니다 . 문이 사라지고 반환 값이 더 일반적인 통화가되므로 식의 반환 값을 무시하는 것이 더 드물기 때문에 "닫는 구분 기호까지 일련의 단계를 열어야합니다"라는 욕구는 자주 발생하지 않습니다. 부작용 단독에 대한 일련의 표현을 평가하는 것은 드 rare니다.

Common Lisp에서 progn형식은 예외입니다 (형제와 마찬가지로).

(progn
  (exp-ignored-return-1)
  (exp-ignored-return-2)
  (exp-taken-return))

여기서는 progn세 식을 순서대로 평가하지만 처음 두 개의 반환 값은 무시합니다. 마지막 닫는 괄호를 그 자체로 작성하는 것을 상상할 수 있지만 마지막 양식은 여기에서 특별하기 때문에 (공통 리스프에서는 특별 하지는 않지만) 별개의 처리로 인해 새로운 것을 추가 할 가능성이 더 큽니다 호출자는 새로운 부작용뿐만 아니라 반환 값의 변화에 ​​영향을 받기 때문에 "중간에 다른 것을 추가하는 것"이 ​​아니라 시퀀스 중간에있는 표현식을 사용합니다.

Lisp 프로그램의 대부분의 부분에서 괄호를 크게 단순화하면 C와 같은 언어와 마찬가지로 함수에 전달되는 인수를 구분할 수 있으며 명령문 블록을 구분할 수는 없습니다 . 같은 이유로 우리는 괄호를 C의 함수 호출에 인수로 묶는 경향을 유지하는 경향이 있으므로 Lisp에서도 같은 그룹을 벗어나는 동기 부여가 적습니다.

괄호를 닫는 것은 여는 형식의 들여 쓰기보다 훨씬 덜 중요합니다. 시간이 지나면 파이썬 프로그래머와 마찬가지로 괄호를 무시하고 모양별로 쓰고 읽는 법을 배웁니다. 그러나 유추로 인해 괄호를 완전히 제거 하는 것이 가치가 있다고 생각하게하지 마십시오 . 아니요,에 대한 토론은 최고입니다 comp.lang.lisp.


2
나는 마지막에 추가하는 것이 드물지 않다고 생각한다 (let ((var1 expr1) more-bindings-to-be-added) ...). 또는(list element1 more-elements-to-be-added)
Alexey

14

도움이되지 않기 때문입니다. 코드 구조를 보여주기 위해 들여 쓰기를 사용합니다. 코드 블록을 분리하려면 실제로 빈 줄을 사용합니다.

Lisp 구문은 일관성이 있으므로 괄호는 프로그래머와 편집기 모두에 대한 들여 쓰기에 대한 결정적인 가이드입니다.

(나에게있어 문제는 오히려 C와 Java 프로그래머가 중괄호를 던지는 것을 좋아하는 이유입니다.)

이러한 연산자를 C와 같은 언어로 사용할 수 있다고 가정하면 다음과 같습니다.

Foo defer_expensive (Thunk cheap, Thunk expensive) {
    if (Foo good_enough = force (cheap)) {
        return good_enough; }
    else {
        return force (expensive); }}

두 개의 닫는 괄호는 두 개의 중첩 레벨을 닫습니다. 구문은 분명히 맞습니다. 파이썬 구문과 유사하게 중괄호는 명시적인 INDENT 및 DEDENT 토큰입니다.

물론 이것은 "하나의 진정한 버팀대 스타일" TM 이 아닐 수도 있지만, 그것은 단지 역사적인 사고와 습관이라고 믿습니다.


2
(일부도 올바른을 닫을 때 또한, 거의 모든 혀짤배기 편집기는 일치하는 괄호 표시 )]반대 또는 그) 당신은 당신이 들여 쓰기 수준을 확인하지 않는 경우에도 적당한 양을 폐쇄 알 수 있도록.
구성자

6
WRT "질문", 두 번째 예의 스타일에서 "닫기"토큰을 닫으면 자동으로 일치하지 않고 텍스트 편집기를 사용하는 경우에도 눈에 쉽게 맞춰 줄 수 있고 무엇이 닫히는 지 확인할 수 있습니다. 강조 기능.
메이슨 휠러

1
@Mason Wheeler 네, 맞습니다.
Chiron

3
TBH, 이것이 나에게 말하는 것은 LISP의 parens는 들여 쓰기가 잘못 인도 될 가능성이 있기 때문에 (C ++과 같이) 대부분 중복되어 있다는 것입니다. 들여 쓰기가 컴파일러에게도 같은 것을 알려 주어야한다는 것을 알려 주면, Haskell과 Python 에서처럼. BTW-C ++의 if / switch / while / what과 동일한 들여 쓰기 레벨로 버팀대를 사용하면 들여 쓰기가 잘못되는 경우를 방지 할 수 있습니다. 들여 쓰기는 그 중괄호와 일치합니다.
Steve314

1
@ Steve314 : 아니요, 들여 쓰기는 중복됩니다. 들여 쓰기는 Python과 Haskell에서도 오도 될 수 있습니다 (일회성 들여 쓰기 또는 표 형식에 대해 생각). 파서도 오도됩니다. 나는 괄호가 작가와 파서를위한 도구이고, 들여 쓰기는 작가와 (인간) 독자를위한 도구라고 생각한다. 즉, 들여 쓰기는 주석이고 괄호는 구문입니다.
Svante

11

그러면 코드가 훨씬 간결 해집니다. 편집기에서의 움직임은 s- 표현식에 의한 것이므로 편집 할 공간이 필요하지 않습니다. 코드는 주로 구분 기호를 따르는 것이 아니라 구조와 문장으로 읽습니다.


답변을 드리겠습니다 :) 고마워.
Chiron

S- 표현식으로 어떤 편집기가 움직입니까? 보다 구체적으로, 어떻게 vim하면 되나요?
hasen

2
@HasenJ vimacs.vim 플러그인 이 필요할 수 있습니다 . : P
Mark C

5

Lispers, 당신은 그것이 될 수 있으므로 hatefully bletcherous, 알고있을 것입니다 특정 quoi를 SAIS NE JE 두 번째 예 :

(defn defer-expensive [cheap expensive]
  (if-let [good-enough (force cheap)]
    good-enough
    (force expensive)
  )
)

처음에 나는 매력의 근원에 손가락을 넣을 수 없어서, 방아쇠가 없다는 것을 깨달았습니다.

(defn defer-expensive [cheap expensive]      
  (if-let [good-enough (force cheap)]
    good-enough   ;   )
    (force expensive) 
  )
)

oil!

이제 Lisp 갱스터 그립을 연습하겠습니다!


2
이것은 내가이 사이트에서 본 가장 재미 있고 과소 평가 된 답변 중 하나입니다. 모자를 기울입니다.
byxor

0

필자의 경우 구분자 전용 줄은 화면 공간을 낭비하고 C로 코드를 작성할 때 다음과 같은 스타일이 있습니다.

if (pred) {
   printf("yes");
   ++yes_votes;
}

사람들은 왜 공간을 절약하기 위해 오프닝 괄호를 "if"와 같은 줄에 넣습니까? "if"에 이미 자신의 줄이있을 때 자체 줄을 갖는 것이 중복되어 보이기 때문입니다.

끝에 괄호를 묶어 같은 결과를 얻습니다. C에서는 문장이 세미콜론으로 끝나고 대괄호 쌍이 이렇게 열리지 않기 때문에 이상하게 보입니다.

{if (pred)
    printf("yes");
}

그것은 끝에서 닫는 버팀대와 같습니다. 이처럼 열립니다

if (pred) {
    printf("yes");
}

'{'& '}'을 (를) 통해 블록과 한계를 명확하게 볼 수 있습니다.

vim처럼 강조 표시하여 괄호와 일치하는 편집기를 사용하면 모든 괄호가 채워진 끝으로 이동하여 쉽게 옮길 수 있으며 모든 오프닝 패런을 일치시키고 중첩 된 리스프 양식을 볼 수 있습니다

(defn defer-expensive [cheap expensive]
    _(if-let [good-enough (force cheap)]
    good-enough
    (force expensive)_))

커서를 중간의 닫는 par에 놓고 if-let의 par은 highlighted을 강조 표시 할 수 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.