TeX、216バイト(4行、それぞれ54文字)
バイト数に関するものではないため、タイプセット出力の品質に関するものです:-)
{\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}
。
区切られていないマクロの対応物は、当然のことながら、区切られたマクロです:)前の定義を少し意味的にすることができます:\def\say #1 to #2.{#1, #2!}
。この場合、パラメーターの後に、いわゆるパラメーターテキストが続きます。このようなパラメーターテキストは、マクロの引数を#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
、1つの単一の制御シーケンスである、2つの文字が続く\he11o
制御シーケンス、文字が続く\he
1
o
ので、1
場合catcode 11ないI didは\catcode`1=11
、その時点から\he11o
1つの制御シーケンスになります。1つの重要なことは、TeXが手元にある文字を最初に見たときにcatcodeが設定され、そのようなcatcodeが凍結されることです... 永遠に!(利用規約が適用される場合があります)
catcode 12など、他のほとんどの文字0"!@*(?,.-+/
です。これらは、紙の上に物を書くためだけに役立つため、最も特別なタイプのcatcodeです。しかし、ちょっと、誰がTeXを使って書いているのですか?!?(再度、契約条件が適用される場合があります)
catcode 13、これは地獄です:)本当に。読むのをやめて、あなたの人生から何かをしてください。catcode 13が何であるか知りたくありません。13日の金曜日を聞いたことがありますか?名前の由来を推測してください!自己責任で続行してください!「アクティブ」キャラクターとも呼ばれるcatcode 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
別の操作です)は、その割り当てがグローバルでない限り、グループが開始される前の状態に復元されます。グループを開始するにはいくつかの方法がありますが、そのうちの1つは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は改行をスペースとして理解します。複数の単語の間に空行ではなく改行を挿入してみてください。次に%
、これらの行の最後にa を追加します。これらの行末スペースを「コメントアウト」していたようです。それでおしまい :)
(並べ替え)コードのアンゴルフ
その長方形を(おそらく)わかりやすいものにしましょう:
{
\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 }
}
各ステップの説明
各行には1つの命令が含まれます。それらを1つずつ分析していきましょう。
{
最初にグループを開始して、いくつかの変更(つまりcatcodeの変更)をローカルに保持し、入力テキストが混乱しないようにします。
\let~\catcode
基本的に、すべての TeX難読化コードはこの命令で始まります。デフォルトでは、プレーンTeXとLaTeXの両方で、~
文字は、さらに使用するためにマクロにすることができる1つのアクティブな文字です。また、TeXコードを変なものにするための最適なツールはcatcodeの変更なので、これが一般的に最良の選択です。代わりに、\catcode`A=13
次のように書くことができます~`A13
(これ=
はオプションです):
~`A13
これでレターA
はアクティブなキャラクターになりました。これを定義して何かを行うことができます。
\defA#1{~`#113\gdef}
A
現在、1つの引数(別の文字である必要があります)を取るマクロです。最初に、引数のcatcodeを13に変更してアクティブにします~`#113
(~
by \catcode
を置き換えてan =
を追加すると、次のようになります\catcode`#1=13
)。最後に、入力ストリームに\gdef
(global \def
)を残します。つまり、A
別のキャラクターをアクティブにし、その定義を開始します。試してみよう:
AGG#1{~`#113\global\let}
AG
最初に「アクティブ化」G
し\gdef
、「次」に続いG
て定義を開始します。の定義はの定義G
と非常に似ていますがA
、代わりにを実行\gdef
する点が異なります\global\let
(の\glet
ようなものはありません\gdef
)。要するに、G
キャラクターを活性化し、それを別のものにする。後で使用する2つのコマンドのショートカットを作成しましょう。
GFF\else
GHH\fi
今の代わりに\else
と\fi
、私たちは簡単に使用することができるF
とH
。はるかに短い:)
AQQ{Q}
ここでA
再び別のマクロを定義するために使用しますQ
。上記のステートメントは基本的に(あまり難読化されていない言語で)行い\def\Q{\Q}
ます。これはそれほど興味深い定義ではありませんが、興味深い機能があります。何らかのコードを壊したい場合を除き、展開されるマクロQ
はQ
それ自体だけなので、一意のマーカーのように機能します(クォークと呼ばれます)。\ifx
条件を使用して、マクロの引数がそのようなクォークであるかどうかをテストできます\ifx Q#1
。
AII{\ifxQ}
そのため、そのようなマーカーを見つけたことを確信できます。この定義では、私は間にスペースを削除していることに注意してください\ifx
とQ
。通常、これはエラーになります(構文のハイライトはそれ\ifxQ
を1つのことと考えていることに注意してください)が、現在Q
はcatcode 13であるため、制御シーケンスを形成できません。ただし、このクォークを展開しないように注意してください。Q
展開すると、Q
どの展開先Q
がどの展開先になり、無限ループに陥ります。
準備が完了したので、適切なアルゴリズムに進んでeht setterlを実行できます。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
、catcodeが変更される前に定義内の文字(catcode 11)としてトークン化されたため、常にletterになるためE
です。ただし、2番目の例では、E
最初にアクティブ\one
にされてから定義されたので、定義にはE
に展開されるcatcode 13 が含まれてい1
ます。
ただし、この事実を見落として、定義を論理的な(ただし機能しない)順序に並べ替えます。以下の段落では、文字と仮定することができB
、C
、D
、およびE
アクティブです。
\gdef\S#1{\iftrueBH#1 Q }
(以前のバージョンに小さなバグがあったことに注意してください。上記の定義に最後のスペースが含まれていませんでした。これを書いているときに気づきました。読み進めてください。 )
まず、ユーザーレベルのマクロを定義します\S
。これは、親しみやすい(?)構文を持つアクティブなキャラクターであってはならないので、gwappins eht setterlのマクロは\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
、IS BHhello world Q␣
。B
に続いH
てが必要で(そうでない場合はTeXがエラーを発生させます)、次のスペースはとの間にhello
ありworld
、#1
単語もそうですhello
。ここで、入力テキストをスペースで分割する必要がありました。イェーイ:Dの拡大B
により、入力ストリームと置き換えから最初のスペースに削除し、すべてのアップHI#1FC#1|BH
で#1
あることhello
:HIhelloFChello|BHworld Q␣
。BH
入力ストリームの後半に新しいものがあることに注意してください。B
後の単語を処理します。この単語が処理B
された後、処理対象の単語がクォークになるまで次の単語が処理されますQ
。後の最後のスペースがQ
区切られたマクロがあるため必要とされているB
必要があり、引数の最後に1を。以前のバージョン(編集履歴を参照)では、使用するとコードが正しく動作しませんでし\S{hello world}abc abc
た(abc
s 間のスペースがなくなります)。
OK、入力ストリームに戻りますHIhelloFChello|BHworld Q␣
。最初に、イニシャルを完了するH
(\fi
)があり\iftrue
ます。これがあります(擬似コード化):
I
hello
F
Chello|B
H
world Q␣
I...F...H
実際にあると思い\ifx Q...\else...\fi
構造。\ifx
テストチェック語(の最初のトークン)がつかま場合であるQ
クォーク。実行するものが他にない場合、実行は終了します。それ以外の場合は次のとおりChello|BHworld Q␣
です。今C
拡張されます:
ACC#1#2|{D#2Q|#1 }
最初の引数はC
、区切り文字のないされ、それは単一のトークンになりブレースない限り、第二引数が区切られ|
、これの拡張後C
(と#1=h
と#2=ello
)入力ストリームであります:DelloQ|h BHworld Q␣
。別のもの|
がそこに置かれ、その後にh
of hello
が置かれることに注意してください。スワッピングの半分が完了しました。最初の文字は最後にあります。TeXでは、トークンリストの最初のトークンを簡単に取得できます。\def\first#1#2|{#1}
を使用すると、単純なマクロが最初の文字を取得します\first hello|
。最後の1つは問題です。TeXは常に「最小の、おそらく空の」トークンリストを引数として取得するため、いくつかの回避策が必要です。トークンリストの次の項目は次のとおりですD
。
ADD#1#2|{I#1FE{}#1#2|H}
このD
マクロは回避策の1つであり、単語が1文字の場合にのみ役立ちます。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
とD
。最初と2番目の引数は区切られず、最後の引数はで区切られ|
ます。このような入力ストリームルックス: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
、次の単語に対してプロセス全体を繰り返すもう1 つの単語です。
}
最後に、すべてのローカル割り当てが取り消されるように、そこから開始されたグループを終了します。地元の割り当ては、文字のcatcodeの変化であるA
、B
、C
、...マクロが行われたので、彼らは彼らの通常の文字の意味に戻っていることを安全にテキストで使用することができます。以上です。ここで\S
定義されたマクロは、上記のようにテキストの処理をトリガーします。
このコードの興味深い点の1つは、完全に拡張可能であることです。つまり、爆発することを心配することなく、引数を移動する際に安全に使用できます。コードを使用して、\if
テストの単語の最後の文字が2番目の文字と同じであるかどうか(何らかの理由で必要な場合)を確認することもできます。
\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
「残っているもの」が空であるかどうかをチェックします。その場合、1文字の単語が見つかった場合、何もしません。それ以外の場合は展開されE
ます。E
単語の最後の文字が見つかるまでトークンリストをループし、見つかった場合はその最後の文字を残し、その後に単語の中央が続き、トークンストリームの最後に最初の文字が続きますC
。
Hello, world!
となり,elloH !orldw
(文字として句読点を交換)またはoellH, dorlw!
(場所に句読点を維持しますか)?