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是一种宏扩展语言。作为示例,您可以定义\def\sayhello#1{Hello, #1!}
然后编写\sayhello{Code Golfists}
以使TeX打印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读取一个字符(由“字符代码”定义)时,它将为该字符分配一个“类别代码”(类别代码,对于朋友:),该字符定义了该字符的含义。字符代码和类别代码的这种组合构成了令牌(例如,此处有更多内容)。我们这里感兴趣的基本上是:
目录代码11,它定义可以组成控制序列的标记(宏的宏伟名称)。默认情况下,所有字母[a-zA-Z]都为catcode 11,因此我可以编写\hello
,这是一个单一的控制序列,而\he11o
该控制序列\he
后面是两个字符1
,然后是字母o
,因为1
它不是catcode 11。从那时\catcode`1=11
起,\he11o
将是一个控制序列。重要的一点是,当TeX第一次看到手边的字符时,就设置了Catcode,并且这样的Catcode被冻结了 ……永远!(条款和条件可能适用)
目录代码12,是大多数其他字符,例如0"!@*(?,.-+/
等等。它们是Catcode 的最特殊类型,因为它们仅用于在纸上书写内容。但是,嘿,谁用TeX编写文字?!(同样,条款和条件可能适用)
目录代码13,这真是地狱:)真的。停止阅读,去做一些你毕生无法做的事情。您不想知道类别代码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
在代码中编写一个!这就是目录代码13。我很平静!让我们继续前进。
好的,现在分组。这很简单。不论分配(\def
是分配操作,\let
除非分配是全局的,否则在组中完成的(我们将进行介绍)是另一个)都将恢复为该组开始之前的状态。有几种开始组的方法,其中一种是使用类别1和2个字符(哦,再次使用类别代码)。默认{
是目录1,或开始组,目录}
2,或结束组。例如:\def\a{1} \a{\def\a{2} \a} \a
打印1 2 1
。外组\a
是1
,那里面它被重新解释2
,当组结束时,就恢复了1
。
该\let
操作是另一个分配操作,例如\def
,但有所不同。随着\def
你定义的宏将扩展到东西,与\let
你创造的已经存在的事物的副本。之后\let\blub=\def
(=
可选),您可以将e
示例的开头从上面的目录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
替换~
by \catcode
并添加an =
和you have:)\catcode`#1=13
。最后,它在输入流中留下一个\gdef
(全局\def
)。简而言之,A
激活另一个字符并开始其定义。让我们尝试一下:
AGG#1{~`#113\global\let}
AG
首先“激活” G
并执行\gdef
,然后由下一个G
开始定义。的定义G
是非常相似的A
,只是不用\gdef
它做了\global\let
(有没有\glet
像\gdef
)。简而言之,G
激活一个角色并使它成为其他角色。让我们为稍后将使用的两个命令创建快捷方式:
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
是一回事),但是由于现在Q
是catcode 13,它无法形成控制序列。但是,请注意不要扩展此夸克,否则将陷入无限循环,因为Q
扩展到Q
哪个扩展到Q
哪个...
现在已经完成了预备工作,我们可以使用适当的算法来进行设置。由于TeX的标记化,该算法必须向后编写。这是因为在您进行定义时,TeX会使用当前设置对定义中的字符标记化(分配目录代码),例如,如果我这样做:
\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
中,定义更改之前,定义中的标记为字母(目录代码11),因此它将始终为letter E
。但是,在第二个示例中,E
首先将其激活,然后\one
才对其进行定义,现在该定义包含目录代码13E
变为扩展为的目录代码1
。
但是,我将忽略这一事实,并对定义重新排序以使其具有逻辑顺序(但不起作用)。在下面的段落,你可以假设信件B
,C
,D
,和E
活跃。
\gdef\S#1{\iftrueBH#1 Q }
(请注意,以前的版本中有一个小错误,上面的定义中没有包含最后的空格。我只是在编写本文时才注意到它。继续阅读,您将了解为什么我们需要那个来正确终止宏。 )
首先,我们定义用户级宏\S
。该字符不应该是具有友好(?)语法的活动字符,因此gwappins setterl的宏是\S
。宏以始终为true的条件开头\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
and 之间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 之间的空格将消失)。
OK,回到输入流:HIhelloFChello|BHworld Q␣
。首先是H
(\fi
)完成了initial \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
(with #1=h
和#2=ello
)展开后,输入流为:DelloQ|h BHworld Q␣
。请注意,另一个|
放置在此处,h
of 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
,它将看到参数为quark并将中断执行,仅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
。第一个和第二个参数是无界的,最后一个由界定|
。输入流看起来像这样: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␣
。ew
哦,等等,这些是什么?正常字母?好家伙!终于找到了字母,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
很抱歉(可能也是如此)罗explanation的解释。我也尝试使非TeXies也尽可能清楚:)
急躁的总结
巨集\S
会在输入端加上一个主动字元B
,该字元会撷取以最后一个空格分隔的标记清单,并将它们传递给C
。C
获取该列表中的第一个令牌并将其移至令牌列表的末尾,然后扩展D
剩余的令牌。D
检查“剩下的”是否为空,在这种情况下,找到一个字母,然后什么也不做;否则扩展E
。E
循环遍历令牌列表,直到找到单词中的最后一个字母,当找到单词时,它离开最后一个字母,然后是单词的中间,然后是令牌流末尾剩下的第一个字母,即C
。
Hello, world!
变成,elloH !orldw
(将标点符号交换为字母)还是oellH, dorlw!
(将标点符号保持在适当的位置)?