Lisp 유사 구문 형식화


23

배경

(진정하고 감동적인 이야기를 바탕으로)

저는 Lisp와 비슷한 언어로 자주 놀았습니다. 나는 그들과 함께 쓰고, 실행하고, 해석하고, 디자인하고, 나를 위해 기계를 쓰도록 만들었습니다 ... 그리고 나를 괴롭히는 것이 있다면, 특정 형식 스타일을 따르지 않는 Lisp를보고 있습니다.

불행히도 일부 텍스트 편집기 ( 기침 XCode 기침 )는 코드를 복사하여 붙여 넣을 때마다 아름다운 탭과 공백을 제거하는 경향이 있습니다 ...

(A
    (B
        (C)
        (D))
    (E))

( ABCDE임의의 기능이있는 곳)

일부 텍스트 편집기는이 멋진 코드를 다음과 같이 정돈 합니다.

(A
(B
(C)
(D))
(E))

엉망이야! 읽을 수 없습니다!

도와 줘요?

도전

이 과제의 목표는 아래에 설명 된 형식으로 줄 바꿈으로 구분 된 일련의 함수를 사용하여 가독성과 우아함을 강조하는보다 아름다운 배열을 반환하는 것입니다.

입력

Farity N인수 의 함수 를 다음과 유사한 구조로 정의합니다 .

(F (G1 ...) (G2 ...) (G3 ...) ... (GN ...))

G1, G2, ..., GN그 자체로 모든 기능이 어디에 있습니까 ? arity 0함수 A는 간단 (A)하지만 arity 2함수 B는 형식입니다(B (...) (...))

코드는 모든 함수의 선행 괄호 앞에 첫 번째 줄 바꿈이있는 일련의 함수로 입력을 가져와야합니다 (첫 번째 함수 제외). 위의 예는 유효한 입력입니다.

당신은 가정 할 수 있습니다 :

  • 괄호는 균형을 이룹니다.
  • 함수는 250 번 이상 들여 쓰기 할 필요가 없습니다.
  • 모든 기능은 괄호로 묶습니다. ()
  • 함수 이름에는 인쇄 가능한 ASCII 문자 만 포함됩니다.
  • 함수 이름에는 괄호 나 공백이 포함되지 않습니다.
  • 입력에 선택적인 후행 줄 바꿈이 있습니다.

출력

코드는 동일한 함수 집합을 출력해야 합니다. 여기서 변경되는 유일한 함수는 선행 괄호 앞에 공백이나 탭을 추가하는 것입니다. 출력은 다음 규칙을 준수해야합니다.

  • 주어진 첫 번째 함수 (및 이후 최상위 함수)에는 선행 공백이 없어야합니다.
  • 함수의 가로 위치에 대한 인수는 해당 함수의 가로 위치 오른쪽에 정확히 하나의 탭입니다.
  • 탭은 구현에 따라 정의되지만 3 칸 이상이어야합니다.
  • 각 줄 다음에 최대 2 개의 공백을 선택적으로 인쇄 할 수 있습니다.

규칙

  • 이것은 코드 골프입니다. 가장 짧은 코드가 승리합니다!
  • 표준 허점 은 허용되지 않습니다.

입력:

(A
(B
(C)
(D))
(E))

산출:

(A
    (B
        (C)
        (D))
    (E))

입력:

(!@#$%^&*
(asdfghjklm
(this_string_is_particularly_long
(...))
(123456789)))
(THIS_IS_TOP_LEVEL_AGAIN
(HERE'S_AN_ARGUMENT))

산출:

(!@#$%^&*
    (asdfghjklm
        (this_string_is_particularly_long
            (...))
        (123456789)))
(THIS_IS_TOP_LEVEL_AGAIN
    (HERE'S_AN_ARGUMENT))

입력:

(-:0
(*:0
(%:0
(Arg:6)
(Write:0
(Read:0
(Arg:30))
(Write:0
(Const:-6)
(Arg:10))))
(%:0
(Const:9)
(/:0
(Const:-13)
(%:0
(Arg:14)
(Arg:0)))))
(WriteArg:22
(-:0
(Const:45)
(?:0
(Arg:3)
(Arg:22)
(Arg:0)))))

산출:

(-:0
    (*:0
        (%:0
            (Arg:6)
            (Write:0
                (Read:0
                    (Arg:30))
                (Write:0
                    (Const:-6)
                    (Arg:10))))
        (%:0
            (Const:9)
            (/:0
                (Const:-13)
                (%:0
                    (Arg:14)
                    (Arg:0)))))
    (WriteArg:22
        (-:0
            (Const:45)
            (?:0
                (Arg:3)
                (Arg:22)
                (Arg:0)))))

핫 네트워크 질문 목록을 작성하신 것을 축하합니다! : D
Alex A.

@AlexA. 만세! 나의 꿈이 실현되었습니다. : D
BrainSteel

함수 이름이 없다면 ()?
coredump

들여 쓰기가> = 3 공백이어야합니까, 아니면 탭이 허용됩니까?
isaacg

@isaacg이 경우 모든 기능의 이름이 지정되었다고 가정 할 수 있습니다. 그리고 OS / 언어가 가로 탭으로 정의한 것은 괜찮습니다. 공백을 사용하는 경우 3 이상이 있어야합니다. 컴퓨터에 도착할 때 명확히하겠습니다. 감사!
BrainSteel

답변:


9

Pyth, 24 20 19 18 바이트

FN.z+*ZC9N~Z-1/N\)

모든 줄에 대한 카운터를 늘리고 지금까지 발생한 총 닫는 괄호 수를 세고 카운터에서 뺍니다. 그런 다음 counter탭으로 들여 쓰기 합니다.


@Downvoter주의해서 설명 하시겠습니까?
orlp

나는 공감하지 않았지만 *4하드 코딩되고 중복 된 환경 설정입니다. FN.z+*ZC9N~Z-1/N\)편집기의 들여 쓰기 너비를 사용하고 1 바이트를 저장할 수 있습니다.
Cees Timmerman

나는 탭이 한 문자 더 짧을 것이라고 동의했다. \<tab>또는 C9.
isaacg

9

공통 리스프 -486 414 바이트 (Rube Goldberg 버전)

(labels((p(x d)(or(when(listp x)(#2=princ #\()(p(car x)d)(incf d)(dolist(a(cdr x))(format t"~%~v{   ~}"d'(t))(p a d))(#2# #\)))(#2# x))))(let((i(make-string-input-stream(with-output-to-string(o)(#1=ignore-errors(do(b c)(())(if(member(setq c(read-char))'(#\( #\) #\  #\tab #\newline):test'char=)(progn(when b(prin1(coerce(reverse b)'string)o))(#2# c o)(setq b()))(push c b))))))))(#1#(do()(())(p(read i)0)(terpri)))))

접근

다른 사람처럼하고 괄호를 손으로 세지 말고 Lisp 독자를 불러서 올바른 길을 가자 :-)

  • 입력 스트림에서 읽고 임시 출력 스트림에 씁니다 .
  • 그렇게하는 동안, 다른 집계 문자 (, )문자열이나 공백.
  • 중간 출력은 구문을 잘 구성한 Common-Lisp 형식 (문자열의 중첩 된 목록)을 포함하는 문자열을 작성하는 데 사용됩니다.
  • 해당 문자열을 입력 스트림으로 사용하여 표준 read함수를 호출하여 실제 목록을 작성하십시오.
  • 전화 p반복적으로 요구 된 형식으로 표준 출력에 기록하는, 그 목록의 각. 특히 문자열은 따옴표없이 인쇄됩니다.

이 접근법의 결과로 :

  1. 입력 형식에는 제한이 적습니다. "한 줄에 하나의 기능"(ugh)이 아니라 임의로 형식이 지정된 입력을 읽을 수 있습니다.
  2. 또한 입력이 제대로 구성되지 않으면 오류가 발생합니다.
  3. 마지막으로 pretty-printing 함수는 구문 분석과 잘 분리되어 있습니다. 다른 방식으로 S-expressing을 다른 방식으로 쉽게 전환 할 수 있습니다 (수직 공간을 중요하게 생각하는 경우).

이 랩퍼를 사용하여 파일에서 읽기 :

(with-open-file (*standard-input* #P"path/to/example/file")
    ...)

결과는 다음과 같습니다.

(!@#$%^&*
    (asdfghjklm
        (this_string_is_particularly_long
            (...))
        (123456789)))
(THIS_IS_TOP_LEVEL_AGAIN
    (HERE'S_AN_ARGUMENT))
(-:0
    (*:0
        (%:0
            (Arg:6)
            (Write:0
                (Read:0
                    (Arg:30))
                (Write:0
                    (Const:-6)
                    (Arg:10))))
        (%:0
            (Const:9)
            (/:0
                (Const:-13)
                (%:0
                    (Arg:14)
                    (Arg:0)))))
    (WriteArg:22
        (-:0
            (Const:45)
            (?:0
                (Arg:3)
                (Arg:22)
                (Arg:0)))))

(여기에서 탭이 공백으로 변환 된 것 같습니다)

정교하게 인쇄 된 (골프 버전)

더 안전한 원본 버전과 달리 입력이 유효 할 것으로 예상합니다.

(labels ((p (x d)
           (or
            (when (listp x)
              (princ #\()
              (p (car x) d)
              (incf d)
              (dolist (a (cdr x)) (format t "~%~v{  ~}" d '(t)) (p a d))
              (princ #\)))
            (princ x))))
  (let ((i
         (make-string-input-stream
          (with-output-to-string (o)
            (ignore-errors
             (do (b
                  c)
                 (nil)
               (if (member (setq c (read-char)) '(#\( #\) #\  #\tab #\newline)
                           :test 'char=)
                   (progn
                    (when b (prin1 (coerce (reverse b) 'string) o))
                    (princ c o)
                    (setq b nil))
                   (push c b))))))))
    (ignore-errors (do () (nil) (p (read i) 0) (terpri)))))

7

망막 , 89 83 바이트

s`.+
$0<tab>$0
s`(?<=<tab>.*).
<tab>
+ms`^((\()|(?<-2>\))|[^)])+^(?=\(.*^((?<-2><tab>)+))
$0$3
<tab>+$
<empty>

여기서 <tab>실제 탭 문자 (0x09)를 <empty>나타내며 빈 줄을 나타냅니다. 교체 후 -s플래그로 위 코드를 실행할 수 있습니다 . 그러나 각 플래그를 자체 소스 파일에 넣을 수 있기 때문에 해당 플래그를 계산하지 않습니다.이 경우 7 개의 줄 바꿈이 추가 소스 파일의 7 페널티 바이트로 바뀝니다.

이 프로그램은 STDIN에 입력하여 결과를 STDOUT에 인쇄하는 전체 프로그램입니다.

설명

모든 라인 쌍은 정규식 대체를 정의합니다. 기본 아이디어는 .NET의 밸런싱 그룹 을 사용하여 현재 수심을 주어진 수까지 세고 그 (전에 많은 탭을 삽입하는 것입니다 (.

s`.+
$0<tab>$0

먼저 입력을 준비합니다. 입력 문자열에서 탭을 찾을 수 없으면 조건부 수의 탭을 실제로 다시 쓸 수 없습니다. 따라서 전체 입력을 탭으로 구분하여 복제합니다. 이 명령 s`은 단일 행 (또는 "dot-all") 수정자를 활성화하여 .개행과도 일치 하도록합니다 .

s`(?<=<tab>.*).
<tab>

이제 탭 뒤의 모든 문자를 탭으로 바꿉니다. 이것은 지금까지 원래 문자열을 수정하지 않고 문자열 끝에 충분한 양의 탭을 제공합니다.

+ms`^((\()|(?<-2>\))|[^)])+^(?=\(.*^((?<-2><tab>)+))
$0$3

이것이 솔루션의 고기입니다. ms다중 회선 모드 (즉, 활성화되도록 ^라인의 시작과 일치) 및 단일 라인 모드. 는 +출력이 변경 멈출 때까지 (이 경우 수단이 패턴은 더 이상 문자열과 일치 때까지)이 대체를 계속 반복하지 않으려면 망막을 알려줍니다.

패턴 자체는 입력의 접두어를 처리되지 않은 것 ((즉, (탭이 없지만 그 앞에 있어야하는 것) 까지 일치 시킵니다. 동시에 스택 높이가 2현재 깊이에 해당하므로 추가 해야하는 탭 수에 맞게 균형 그룹을 사용하여 접두사 깊이를 결정합니다 . 이것이이 부분입니다 :

((\()|(?<-2>\))|[^)])+

이 중 하나와 일치 (에 밀어, 2스택, 또는 그것은 일치 )으로부터 마지막 캡처를 보여주고, 2스택, 또는 뭔가 다른 잎의 손길이 닿지 않은 스택을 일치합니다. 괄호는 균형이 보장되므로 빈 스택에서 팝을 시도하는 것에 대해 걱정할 필요가 없습니다.

다음과 같이 문자열을 살펴보고 처리되지 않은 프로세스 (를 발견 하면 미리보기는 문자열의 끝으로 건너 뛰고 스택 3에서 2비어있을 때까지 팝업에서 탭을 그룹으로 캡처합니다 .

(?=\(.*^((?<-2><tab>)+))

+in 을 사용하여 하나 이상의 탭을 일치 항목에 삽입해야 패턴이 어떤 항목과도 ​​일치하는지 확인합니다. 루트 수준 함수가 여러 개인 경우 무한 루프를 피할 수 있습니다.

<tab>+$
<empty>

마지막으로 문자열 끝에있는 도우미 탭을 제거하여 결과를 정리합니다.


이것은 매우 시원합니다. 잘 했어! Retina를 보는 것은 항상 기쁜 일입니다.
BrainSteel

6

C : 95 94 자

아직 골프를 타지 않았으며 질문에서 탭을 사용할 수 있는지 확실하지 않습니다.

i,j;main(c){for(;putchar(c=getchar()),c+1;i+=c==40,i-=c==41)if(c==10)for(j=i;j--;putchar(9));}

언 골프 드 :

i,j;
main(c){
  for(
    ;
    putchar(c=getchar()),
    c+1;
    i+=c==40,
    i-=c==41
  )
    if(c==10)
      for(
        j=i;
        j--;
        putchar(9)
      );
}

편집 : EOF에서 종료되도록했습니다.


탭은 완벽하게 허용됩니다.
BrainSteel

2
if(c<11)대신에 사용할 수 if(c==10)있습니까?
Digital Trauma

5

줄리아, 103 99 97 94 88 바이트

p->(i=j=0;for l=split(p,"\n") i+=1;println("\t"^abs(i-j-1)*l);j+=count(i->i=='\)',l)end)

문자열을 허용하고 들여 쓰기 된 버전을 인쇄하는 이름없는 함수를 정의합니다. 호출하려면 이름을 지정하십시오 (예 :) f=p->.... 입력은 유효한 Julia 문자열 $이어야 하므로 달러 기호 ( )를 이스케이프해야합니다.

언 골프 + 설명 :

function f(p)
    # Set counters for the line number and the number of close parens
    i = j = 0

    # Loop over each line of the program
    for l in split(p, "\n")
        # Increment the line number
        i += 1

        # Print the program line with |i-j-1| tabs
        println("\t"^abs(i-j-1) * l)

        # Count the number of close parens on this line
        j += count(i -> i == '\)', l)
    end
end

예를 들어, 네 개의 공백 세트 각각을 가장하는 것은 탭입니다.

julia> f("(A
(B
(C)
(D))
(E))")

(A
    (B
        (C)
        (D))
    (E))

어떤 제안이라도 환영합니다!


4

하스켈, 83 81

unlines.(scanl(\n s->drop(sum[1|')'<-s])$n++['\t'|'('<-s])"">>=zipWith(++)).lines

매우 포인트 무료 솔루션입니다.


그냥 떨어 뜨릴 수 있습니다 h=.
Will Ness

3

펄, 41

$_="\t"x($i-$j).$_;$i+=y/(/(/;$j+=y/)/)/

40문자 +1에 대한 -p.

로 실행 :

cat input.txt | perl -pe'$_="\t"x($i-$j).$_;$i+=y/(/(/;$j+=y/)/)/'

3

파이썬 2-88 78 바이트

매우 간단하고 짧지 않은 솔루션 :

l=0
for x in raw_input().split():g=x.count;d=l*'\t'+x;l+=g("(")-g(")");print d

몇 가지 팁 : 1) 1 바이트 '\t'대신 사용하여 ' '1 바이트를 저장할 수 있습니다 . 2) 필요에 할당 없습니다 input.split()그것이에만 동일 (한 번 사용 이후, 변수 c뿐만 아니라, d--just 이동 print) 문; 3) 연산자 우선 순위는 괄호 l*c가 필요하지 않음을 의미 합니다. 또한 f아무것도 사용되지 않는 것 같습니다 . 이전 버전의 유물입니까?
DLosc

또한 이것이 Python 2 인 경우 raw_input대신 대신 사용해야 합니다 input(그리고 괄호를 잊지 마십시오!).
DLosc

2

CJam, 20 바이트

r{_')e=NU)@-:U9c*r}h

CJam 통역사 에서 온라인으로 사용해보십시오 .

작동 원리

r                    e# Read a whitespace-separated token R from STDIN.
{                 }h e# Do, while R is truthy: 
  _')e=              e#   Push C, the number of right parentheses in R. 
       NU            e#   Push a linefeed and U (initially 0).
         )@-         e#   Compute U + 1 - C.
            :U       e#   Save in U.
              9c*    e#   Push a string of U tabulators.
                 r   e#   Read a whitespace-separated token R from STDIN.
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.