TeX, 216 바이트 (4 줄, 각 54 자)
바이트 수에 관한 것이 아니기 때문에 typeset 출력의 품질에 관한 것입니다 :-)
{\let~\catcode~`A13 \defA#1{~`#113\gdef}AGG#1{~`#1 13%
\global\let}GFF\elseGHH\fiAQQ{Q}AII{\ifxQ}AEE#1#2#3|{%
I#3#2#1FE{#1#2}#3|H}ADD#1#2|{I#1FE{}#1#2|H}ACC#1#2|{D%
#2Q|#1 }ABBH#1 {HI#1FC#1|BH}\gdef\S#1{\iftrueBH#1 Q }}
온라인으로 사용해보십시오! (오버 리프; 작동 방식을 잘 모름)
전체 테스트 파일 :
{\let~\catcode~`A13 \defA#1{~`#113\gdef}AGG#1{~`#1 13%
\global\let}GFF\elseGHH\fiAQQ{Q}AII{\ifxQ}AEE#1#2#3|{%
I#3#2#1FE{#1#2}#3|H}ADD#1#2|{I#1FE{}#1#2|H}ACC#1#2|{D%
#2Q|#1 }ABBH#1 {HI#1FC#1|BH}\gdef\S#1{\iftrueBH#1 Q }}
\S{swap the a first and last letters of each word}
pwas eht a tirsf dna tasl setterl fo hace dorw
\S{SWAP THE A FIRST AND LAST LETTERS OF EACH WORD}
\bye
산출:
LaTeX의 경우 상용구가 필요합니다.
\documentclass{article}
\begin{document}
{\let~\catcode~`A13 \defA#1{~`#113\gdef}AGG#1{~`#1 13%
\global\let}GFF\elseGHH\fiAQQ{Q}AII{\ifxQ}AEE#1#2#3|{%
I#3#2#1FE{#1#2}#3|H}ADD#1#2|{I#1FE{}#1#2|H}ACC#1#2|{D%
#2Q|#1 }ABBH#1 {HI#1FC#1|BH}\gdef\S#1{\iftrueBH#1 Q }}
\S{swap the a first and last letters of each word}
pwas eht a tirsf dna tasl setterl fo hace dorw
\S{SWAP THE A FIRST AND LAST LETTERS OF EACH WORD}
\end{document}
설명
TeX는 이상한 짐승입니다. 정상적인 코드를 읽고 이해하는 것은 그 자체로 위업입니다. 난독 화 된 TeX 코드에 대한 이해는 몇 단계 더 진행됩니다. TeX를 모르는 사람들도 이해할 수 있도록 노력하겠습니다. 따라서 시작하기 전에 TeX에 대한 몇 가지 개념을 통해 쉽게 따라 할 수 있습니다.
절대 TeX 초보자를위한 (그렇지 않은)
첫째,이 목록에서 가장 중요한 항목은 팝 문화 가 그렇게 생각 하더라도 코드가 사각형 모양 일 필요 는 없다는 것 입니다.
TeX는 매크로 확장 언어입니다. 예를 들어 TeX를 인쇄하도록 정의한 \def\sayhello#1{Hello, #1!}
다음 쓸 \sayhello{Code Golfists}
수 있습니다 Hello, Code Golfists!
. 이것을 "무제한 매크로"라고하며이를 중괄호로 묶는 첫 번째 (이 경우에만) 매개 변수를 제공합니다. TeX는 매크로가 인수를 잡을 때 이러한 중괄호를 제거합니다. 당신은 9 개의 매개 변수를 사용할 수 있습니다 \def\say#1#2{#1, #2!}
후 \say{Good news}{everyone}
.
undelimited 매크로의 대응은, 당연히, 사람을 구분됩니다 : 당신은 이전의 정의는 조금 더 만들 수있는 의미 론적 : \def\say #1 to #2.{#1, #2!}
. 이 경우 매개 변수 뒤에 소위 매개 변수 text가 있습니다. 이러한 매개 변수 텍스트는 매크로의 인수를 구분합니다 ( #1
로 구분되고 ␣to␣
공백이 포함되고 #2
로 구분됨 .
). 그 정의 후에는을 쓸 수 \say Good news to everyone.
있으며으로 확장됩니다 Good news, everyone!
. 좋은가요? :) 그러나 구분 된 논거는 ( Texbook을 인용 )“ {...}
이 매개 변수가 아닌 특정 토큰 목록에 의해 입력에 이어 올바르게 중첩 된 그룹이 있는 가장 짧은 (비어있을 수있는) 토큰 시퀀스입니다 ”입니다. 이것은\say Let's go to the mall to Martin
이상한 문장을 만들 것입니다. 이 경우 "숨기기"첫 번째로해야 할 것 ␣to␣
와 {...}
: \say {Let's go to the mall} to Martin
.
여태까지는 그런대로 잘됐다. 이제 상황이 이상해지기 시작합니다. TeX는 문자 ( "문자 코드"로 정의 됨)를 읽을 때 해당 문자에 해당 문자의 의미를 정의하는 "범주 코드"(친구를위한 범주 코드)를 할당합니다. 이러한 문자와 범주 코드의 조합은 토큰을 만듭니다 ( 예를 들어 여기 에서 자세히 설명). 여기서 우리에게 관심있는 것은 기본적으로 다음과 같습니다.
catcode 11 은 제어 시퀀스 (매크로의 포쉬 이름)를 구성 할 수있는 토큰을 정의합니다. 내가 쓸 수 있도록 기본적으로 모든 문자 [a-zA-Z]는, catcode 11있는 \hello
동안, 하나의 제어 시퀀스 인 \he11o
제어 순서입니다 \he
두 문자 다음에 1
문자 다음에이 o
있기 때문에, 1
경우 catcode 11 아닙니다 I 한 \catcode`1=11
에 그 시점에서, \he11o
하나 개의 제어 순서 일 것이다. 중요한 것은 TeX가 처음 등장하는 캐릭터를 볼 때 고양이 코드가 설정되고 그러한 고양이 코드가 정지 된다는 것입니다 . (약관이 적용될 수 있습니다)
catcode 12 는 대부분 다른 문자 0"!@*(?,.-+/
등입니다. 그들은 종이에 물건을 쓸 때만 사용되므로 가장 특수한 유형의 catcode입니다. 그런데 TeX를 쓰는 사람은 누구입니까?!? (이용 약관이 적용될 수 있음)
catcode 13 , 지옥 :) 정말. 독서를 멈추고 인생에서 무언가를하십시오. catcode 13이 무엇인지 알고 싶지 않습니다. 13 일 금요일에 들어 본 적이 있습니까? 이름이 어디서 왔는지 맞춰보세요! 당신의 자신의 위험에 계속! "활성"문자라고도하는 고양이 코드 13 문자는 더 이상 문자가 아니라 매크로 자체입니다! 매개 변수를 갖도록 정의하고 위에서 본 것과 같이 확장 할 수 있습니다. 당신이 후에 \catcode`e=13
당신이 생각하는 당신이 할 수있는 \def e{I am the letter e!}
,하지만. 당신. 안돼! e
더 이상없는 편지는, 그래서 \def
하지 않는 것입니다 \def
당신이 알고, 그것이 \d e f
! 당신이 말하는 다른 편지를 고르세요? 괜찮아! \catcode`R=13 \def R{I am an ARRR!}
. 잘 지미, 해봐! 나는 당신이 그 일을 감히하고 R
코드를 작성합니다! 이것이 catcode 13입니다. 나는 차분하다! 계속 갑시다.
자 이제 그룹화합니다. 이것은 매우 간단합니다. 그룹에서 수행 된 모든 할당 ( \def
할당 작업, \let
다른 작업)은 그룹에 수행 된 할당이 전역이 아닌 한 해당 그룹이 시작되기 이전의 상태로 복원됩니다. 그룹을 시작하는 방법에는 여러 가지가 있으며 그 중 하나는 catcode 1 및 2 문자입니다 (다시 catcodes). 기본적 {
으로 catcode 1 또는 begin-group이고 }
catcode 2 또는 end-group입니다. 예 : \def\a{1} \a{\def\a{2} \a} \a
이 인쇄 1 2 1
. 그룹 외부 \a
는 1
이고 내부는로 다시 정의되었으며 2
그룹이 종료되면로 복원되었습니다 1
.
이 \let
작업은과 같은 다른 할당 작업 \def
이지만 다소 다릅니다. 으로 \def
당신은 정의 와 함께, 물건 확장됩니다 매크로 \let
가 이미 존재하는 것들의 복사본을 만듭니다. 이후 \let\blub=\def
( =
선택 사항) e
위의 catcode 13 항목에서 예제 시작을 변경하여 \blub e{...
재미있게 사용할 수 있습니다. 또는 더 나은 방법으로 물건을 깨는 대신 예제를 고칠 수 있습니다 (보시겠습니까!) . 빠른 질문 : 이름을 바꿀 수 있습니까?R
\let\newr=R \catcode`R=13 \def R{I am an A\newr\newr\newr!}
\newR
마지막으로 소위“스퓨리어스 스페이스”. 이것은 “스퓨리어스 스페이스”질문에 대한 답변으로 TeX-LaTeX Stack Exchange 에서 얻은 명성 이 고려되지 않아야한다고 주장하는 사람들이 있고 다른 사람들은 전적으로 동의하지 않기 때문에 금기 사항 입니다. 누구에 동의하십니까? 베팅하세요! 한편 : TeX는 줄 바꿈을 공백으로 이해합니다. 빈 줄이 아닌 줄 바꿈을 사용하여 여러 단어를 쓰십시오 . 이제이 %
줄의 끝에를 추가하십시오 . 마치이 줄 끝 공간을 "설명"하는 것과 같습니다. 그게 다야 :)
코드 풀기
사각형을 쉽게 따라하기 쉽게하자 :
{
\let~\catcode
~`A13
\defA#1{~`#113\gdef}
AGG#1{~`#113\global\let}
GFF\else
GHH\fi
AQQ{Q}
AII{\ifxQ}
AEE#1#2#3|{I#3#2#1FE{#1#2}#3|H}
ADD#1#2#3|{I#2FE{#1}#2#3|H}
ACC#1#2|{D{}#2Q|#1 }
ABBH#1 {HI#1FC#1|BH}
\gdef\S#1{\iftrueBH#1 Q }
}
각 단계에 대한 설명
각 줄에는 하나의 단일 명령이 포함됩니다. 하나씩 해보자.
{
먼저 입력 텍스트를 엉망으로 만들지 않도록 일부 변경 사항 (즉, catcode 변경 사항)을 로컬로 유지하기 위해 그룹을 시작합니다.
\let~\catcode
기본적으로 모든 TeX 난독 화 코드는이 명령어로 시작합니다. 기본적으로 일반 TeX 및 LaTeX에서 ~
캐릭터는 나중에 사용할 수 있도록 매크로로 만들 수있는 하나의 활성 캐릭터입니다. TeX 코드를 이상하게 만드는 가장 좋은 도구는 catcode 변경 사항이므로 일반적으로 이것이 최선의 선택입니다. 이제 \catcode`A=13
우리는 다음과 같이 쓸 수 있습니다 ~`A13
( =
선택 사항).
~`A13
이제 글자 A
는 활동적인 캐릭터이며, 우리는 그것을하기 위해 그것을 정의 할 수 있습니다 :
\defA#1{~`#113\gdef}
A
이제 하나의 인수 (다른 문자 여야 함)를 취하는 매크로입니다. 첫 번째 인수의 catcode은 활성화합니다 (13)로 변경됩니다 : ~`#113
합니다 (교체 ~
로 \catcode
하고, 추가 =
하고, 당신은 : \catcode`#1=13
). 마지막으로 입력 스트림에 \gdef
(global \def
)을 남깁니다 . 즉, A
다른 캐릭터를 활성화하고 정의를 시작합니다. 해 봅시다:
AGG#1{~`#113\global\let}
AG
먼저 "활성화" G
하고 수행 \gdef
하고 다음 G
에 정의가 시작됩니다. 의 정의는 G
매우 유사하다 A
의, 대신에를 제외시켰다 \gdef
그것은이하는 \global\let
(A가없는 \glet
등 \gdef
). 한마디 G
로 캐릭터를 활성화하고 다른 캐릭터로 만듭니다. 나중에 사용할 두 가지 명령에 대한 바로 가기를 만들어 보겠습니다.
GFF\else
GHH\fi
이제 대신은 \else
과 \fi
우리는 간단하게 사용할 수 있습니다 F
및 H
. 훨씬 더 짧은 :)
AQQ{Q}
이제 A
다시 매크로를 사용 하여 다른 매크로를 정의 Q
합니다. 위의 진술은 기본적으로 (덜 난독 한 언어로) 수행 \def\Q{\Q}
합니다. 이것은 굉장히 흥미로운 정의는 아니지만 흥미로운 특징을 가지고 있습니다. 일부 코드를 깨지 않으려는 경우 확장되는 유일한 매크로 Q
는 Q
자체이므로 고유 마커처럼 작동합니다 ( Quark ). \ifx
조건부를 사용하여 매크로의 인수가 다음과 같은 쿼크인지 테스트 할 수 있습니다 \ifx Q#1
.
AII{\ifxQ}
그런 마커를 찾은 것을 확신 할 수 있습니다. 이 정의에서 \ifx
와 사이의 공백을 제거했습니다 Q
. 일반적으로 이것은 오류로 이어질 것입니다 (구문 하이라이트 \ifxQ
는 한 가지 라고 생각합니다 ). 현재 Q
catcode 13이므로 제어 시퀀스를 형성 할 수 없습니다. 그러나,이 쿼크를 확장하거나 때문에 무한 루프에 갇혀거야하지 않도록주의 Q
가 팽창가 Q
되는 확장 Q
된 ...
예비 작업이 완료되었으므로 적절한 알고리즘으로 이동하여 설정을 수행 할 수 있습니다. TeX의 토큰 화로 인해 알고리즘을 거꾸로 작성해야합니다. 정의를 할 때 TeX는 현재 설정을 사용하여 정의의 문자에 토큰 화 (catcodes 할당)하므로 예를 들어 다음과 같은 경우입니다.
\def\one{E}
\catcode`E=13\def E{1}
\one E
E1
정의의 순서를 변경하면 출력은입니다 .
\catcode`E=13\def E{1}
\def\one{E}
\one E
출력은 11
입니다. 첫 번째 예 E
에서 정의 의 in은 catcode가 변경되기 전에 문자 (catcode 11)로 토큰 화 되었기 때문에 항상 letter가 E
됩니다. 그러나 두 번째 예에서는 E
먼저 활성화 \one
된 후에 만 정의되었으며 이제 정의에는 E
로 확장 되는 catcode 13 이 포함되어 있습니다 1
.
그러나 나는이 사실을 간과하고 논리적 인 (그러나 작동하지 않는) 순서를 갖도록 정의를 재정렬합니다. 다음 단락에서는 글자가 있다고 가정 할 수 있습니다 B
, C
, D
, 및 E
활성화됩니다.
\gdef\S#1{\iftrueBH#1 Q }
(이전 버전에는 작은 버그가 있었으며, 위의 정의에 마지막 공간이 포함되어 있지 않습니다.이 글을 쓰는 동안 만 눈치 Read습니다. 계속 읽으면 매크로를 제대로 종료하기 위해 왜 필요한지 알 수 있습니다. )
먼저 사용자 수준 매크로를 정의합니다 \S
. 이 문자는 친숙한 (?) 구문을 갖기 위해 활성 문자가 아니어야하므로 setterl의 gwappins에 대한 매크로는 \S
입니다. 매크로는 항상 참인 조건부로 시작하고 \iftrue
(이유가 분명해질 것임) B
매크로 를 호출 한 다음 H
(이전에 정의한 )을 호출하여 \fi
를 일치시킵니다 \iftrue
. 그런 다음 매크로의 인수 #1
뒤에 공백과 쿼크가옵니다 Q
. 을 사용한다고 가정 \S{hello world}
하면 다음과 입력 스트림을같아야합니다.\iftrue BHhello world Q␣
(나는 ␣
이전 버전의 코드에서했던 것처럼 사이트의 렌더링이 그것을 먹지 않도록 마지막 공간을 교체했습니다 .) \iftrue
사실이므로 확장되고로 남습니다 BHhello world Q␣
. TeX은 않습니다 하지 제거 \fi
( H
) 조건을 평가 한 후이 때까지 대신이 그것을 잎 \fi
입니다 실제로 확대했다. 이제 B
매크로가 확장되었습니다.
ABBH#1 {HI#1FC#1|BH}
B
매개 변수 텍스트 가로 구분 된 매크로H#1␣
이므로 인수는 H
공백 사이 에 있습니다. 확장하기 전에 입력 스트림 위의 예를 계속하면 B
입니다 BHhello world Q␣
. B
따른다 H
는 (달리 텍스 에러를 제기 할)로한다, 그 다음 공간 사이 hello
하고 world
있으므로, #1
단어이다 hello
. 그리고 여기에 입력 텍스트를 공백으로 분할해야합니다. 야호 :의 D를 확장 B
하여 입력 스트림을 대체에서 첫 번째 공간으로 제거합니다 모든 것을 최대 HI#1FC#1|BH
로 #1
되는 hello
: HIhelloFChello|BHworld Q␣
. BH
입력 스트림에 꼬리 재귀를 수행하기 위해 나중에 새로운 것이 있음에 유의하십시오.B
나중에 단어를 처리합니다. 이 단어가 처리 된 후 처리 B
될 단어가 쿼크가 될 때까지 다음 단어가 처리 됩니다 Q
. Q
구분 된 매크로 는 인수의 끝에 하나 가 필요하므로 마지막 공백 이 필요 B
합니다 . 이전 버전 (편집 기록 참조)을 사용하면 코드가 제대로 작동하지 않습니다 \S{hello world}abc abc
( abc
s 사이의 공백 이 사라짐).
다시 입력 스트림으로 돌아갑니다 HIhelloFChello|BHworld Q␣
. 먼저 초기를 완료하는 H
( \fi
)가 \iftrue
있습니다. 이제 우리는 (의사 코드)이 있습니다 :
I
hello
F
Chello|B
H
world Q␣
I...F...H
생각은 사실입니다 \ifx Q...\else...\fi
구조. \ifx
워드 (제 1의 토큰) 경우가 잡고 테스트 검사가있다 Q
쿼크. 수행 할 다른 작업이없고 실행이 종료되면 남은 것은 다음과 같습니다 Chello|BHworld Q␣
.. 이제 C
확장되었습니다 :
ACC#1#2|{D#2Q|#1 }
의 첫 C
번째 인수는 구분되지 않으므로 단일 토큰이 |
되므로 두 번째 인수는로 구분 되므로 입력 스트림 의 C
(with #1=h
and #2=ello
) 확장 후는 DelloQ|h BHworld Q␣
입니다. 주의는 또 다른 것을 |
거기에 넣어, 그리고 h
의는 hello
그 이후가됩니다. 스와핑의 절반이 완료됩니다. 첫 글자는 끝입니다. TeX에서는 토큰 목록의 첫 번째 토큰을 쉽게 얻을 수 있습니다. 간단한 매크로 \def\first#1#2|{#1}
는 사용할 때 첫 글자를 가져옵니다 \first hello|
. TeX는 항상“가장 작고 비어있는”토큰 목록을 인수로 가져 오기 때문에 문제가되므로 몇 가지 해결 방법이 필요합니다. 토큰 목록의 다음 항목은 다음과 D
같습니다.
ADD#1#2|{I#1FE{}#1#2|H}
이 D
매크로는 해결 방법 중 하나이며 단어에 단일 문자가있는 경우에만 유용합니다. hello
우리 대신에 있다고 가정하자 x
. 이 경우, 입력 스트림이 될 것이다 DQ|x
다음 D
(확장과 #1=Q
, 및 #2
비우기) IQFE{}Q|Hx
. 이것은 I...F...H
( \ifx Q...\else...\fi
) 블록 과 유사 B
합니다. 인수는 쿼크이며 타입 설정을 위해서만 실행을 중단합니다 x
. 다른 경우 ( hello
예로 돌아가서 ) D
는 (와 #1=e
으로 #2=lloQ
)를 다음 과 같이 확장 IeFE{}elloQ|Hh BHworld Q␣
합니다. 다시 한 번 I...F...H
확인 Q
하지만 실패하고 \else
분기를 수행합니다 E{}elloQ|Hh BHworld Q␣
. 이제이 부분의 마지막 부분은E
매크로가 확장됩니다.
AEE#1#2#3|{I#3#2#1FE{#1#2}#3|H}
여기의 매개 변수 텍스트는 C
and와 매우 유사합니다 D
. 첫 번째와 두 번째 인수는 구분되지 않으며 마지막 인수는로 구분됩니다 |
. 입력 스트림은 다음과 같습니다 : E{}elloQ|Hh BHworld Q␣
, E
확장 ( #1
비우기 #2=e
,, 및 #3=lloQ
) : IlloQeFE{e}lloQ|HHh BHworld Q␣
. 다른 I...F...H
블록은 쿼크를 확인합니다 (을보고 l
반환 false
) : E{e}lloQ|HHh BHworld Q␣
. 이제 E
다시 #1=e
비 웁니다 ( 공백 #2=l
, 및 #3=loQ
) IloQleFE{el}loQ|HHHh BHworld Q␣
. 그리고 다시 I...F...H
. 매크로 Q
는 마지막으로 발견되고 true
분기가 수행 될 때까지 E{el}loQ|HHHh BHworld Q␣
-> IoQlelFE{ell}oQ|HHHHh BHworld Q␣
-> E{ell}oQ|HHHHh BHworld Q␣
-> 반복 IQoellFE{ello}Q|HHHHHh BHworld Q␣
됩니다. 이제 쿼크가 발견되고 조건부로 확장됩니다 oellHHHHh BHworld Q␣
. 휴
잠깐, 이것들은 무엇입니까? 정상적인 편지? 오 소년! 문자가 마침내 발견되고 TeX가 oell
기록한 다음 H
( \fi
) 무리 가 발견되고 확장되어 (아무것도) 입력 스트림을 다음과 같이 남겨 둡니다 oellh BHworld Q␣
. 이제 첫 번째 단어는 첫 번째와 마지막 문자가 바뀌었고 TeX가 다음에 찾는 B
것은 다음 단어에 대한 전체 프로세스를 반복하는 단어입니다.
}
마지막으로 모든 지역 과제가 취소되도록 그룹이 다시 시작됩니다. 지역 할당이 문자의 catcode 변화이다 A
, B
, C
, ... 매크로를 만들어 된 그래서 그들은 그들의 정상적인 편지 의미로 돌아갈 것을 안전하게 텍스트로 사용할 수 있습니다. 그리고 그게 다야. 이제 \S
다시 정의 된 매크로는 위와 같이 텍스트 처리를 시작합니다.
이 코드에서 흥미로운 점은 코드가 완전히 확장 가능하다는 것입니다. 즉, 폭발 할 염려없이 인수를 이동하는 데 안전하게 사용할 수 있습니다. 코드를 사용하여 \if
테스트 에서 단어의 마지막 문자가 두 번째와 같은지 여부를 확인할 수 있습니다 (필요한 이유는 무엇이든) .
\if\S{here} true\else false\fi % prints true (plus junk, which you would need to handle)
\if\S{test} true\else false\fi % prints false
(아마도) 말로 설명해 줘서 죄송합니다. 비 TeXies에서도 가능한 한 명확하게하려고했습니다. :)
참을성이없는 사람을위한 요약
이 매크로 \S
는 입력 B
에 마지막 문자로 구분 된 토큰 목록을 잡고에 전달 하는 활성 문자 를 추가합니다 C
. C
해당 목록의 첫 번째 토큰을 가져 와서 토큰 목록의 끝으로 이동하고 D
남은 항목으로 확장 합니다. D
"남은 내용"이 비어 있는지 확인합니다.이 경우 한 글자로 된 단어가 발견되면 아무 것도하지 않습니다. 그렇지 않으면 확장됩니다 E
. E
단어의 마지막 문자를 찾을 때까지 토큰 목록을 반복합니다. 마지막 단어가 나오면 단어의 중간이 뒤 따르고 그 뒤에 토큰 스트림의 끝에 남은 첫 번째 문자가옵니다. C
.
Hello, world!
가된다,elloH !orldw
(편지로 구두점을 교환) 또는oellH, dorlw!
(장소에 구두점을 유지)?