通用可重用代码的 LaTeX 实用程序/模板

通用可重用代码的 LaTeX 实用程序/模板

我经常发现自己对具有以下语义的 LaTeX 实用程序很不以为然:

在文件中path/foo.tex

\仅一次{
   % 仅在第一次包含文件时插入一次的代码。
   ...
}

% 每次包含时插入的代码。
...

\OnlyFor{实例-A}{
   % 仅当文件包含“instance-A”参数时才插入的代码。
   ...
}

\OnlyFor{实例-B,实例-C}{
   % 仅当文件包含“instance-B”时才插入的代码
   % 或 'instance-C' 参数。
   ...
}

% 每次包含都会插入更多代码。
...

...

在主 .tex 文件中:

\开始{文档}
...

% 在“实例-A”模式下插入 foo 代码。
\InsertModule{路径/foo}{实例-A}

...

% 在“实例-B”模式下插入 foo 代码。
\InsertModule{路径/foo}{实例-B}

...

% 在“instance-C”模式下插入 foo 代码。
\InsertModule{路径/foo}{实例-C}

...
\结束{文档}

也就是说,foo.tex其行为类似于带有静态一次性组件(用于初始化等)的命令,以及控制插入代码的哪些部分的单个参数。但是,代码是直接注入的,没有特殊作用域(即,命令将使用foo.tex和修改与基础文件相同的计数器)。

除了促进代码重用之外,我在这里寻找的主要功能是简单、干净的代码在基础文件和中foo.tex。与上面的例子类似,其中所有的逻辑、状态、定义、条件、键查找等都被转移到类文件等中。

理想的效用还具有以下属性:

  • 基础文件中的设置仅限于\input\usepackage命令和(可选)单个初始化命令,例如\InitModule{path/foo}

  • 可以在同一个基础文件中使用多个模块,例如

    \InsertModule{路径/foo}{实例-A}
    \InsertModule{另一个/更长/路径/栏}{另一个实例}
    每个模块都保留自己的持久状态\OneTimeOnly(可能与其唯一的文件路径相关联)。我意识到这在 LaTeX 中可能是不可能的,一个可接受的替代方案是必须编写
    \DefineModule{路径/foo}{
       \仅一次{ ... }
       \OnlyFor{实例-A}{...}
    }
    或者
    \OneTimeOnly{路径/foo}{...}
    \OnlyFor{实例-A}{...}
    foo.tex

  • 传递给模块的参数(例如instance-A)可以包含数字和以下至少一个:连字符、空格或下划线

  • 传递给模块的参数可以在模块中使用命令或令牌访问,例如\ForArg#1。甚至更好:如果命令\InsertModule可以附加注入代码可访问的其他参数,例如\InsertModule{path/foo}{instance-A}{\lambda},第二个参数可以作为\ForArg{2}或访问#2,等等。

  • 代码注入可以在数学模式下进行(这完全是可选的,但通常很有用)

我意识到这是一项艰巨的任务,我本来打算自己编写这样一个实用程序,但是对于不熟悉 TeX 宏、包等编码的程序员,一直给出的宏编写建议是:

是否有现有的包可以实现此功能?

如果没有的话,我可以向任何 TeX 专家求助,为我提供一个类等来实现此行为吗?我将永远感激您。:)

答案1

这实际上比您想象的要简单。

  • 如果您将\makeatletter和之间的所有内容放在\makeatother一个单独的文件中,则可以将其用作单个包含文件。

  • \OnlyOnce多个模块具有命令的持久状态

  • “instance”参数可以包含数字、连字符、空格或下划线。

  • 可以使用宏访问实例参数\Instance。您可以传递额外的键值样式参数,这些参数可以使用命令在模块中访问\GetKeyValue{some name}。您可以添加对默认值、执行其他配置任务的键等的支持。

  • 我认为代码注入在数学模式下工作正常,但还没有测试过。

\documentclass{article}

\makeatletter
    \usepackage{pgfkeys}
\pgfkeys{/generic-reusable/.unknown/.code={%
    \expandafter\edef\csname gr-arg-\pgfkeyscurrentkey\endcsname{\unexpanded\expandafter{\pgfkeyscurrentvalue}}%
}}

\def\gr@markincluded#1{\global\expandafter\let\csname gr-file-#1-alreadyincluded\endcsname\empty}
\def\gr@ifincluded#1{\@ifundefined{gr-file-#1-alreadyincluded}}

\def\OneTimeOnly{%
    \gr@ifincluded{\gr@filename}%
        {\@firstofone}%
        {\@gobble}%
}


\def\OnlyFor#1{%
    \gr@OnlyFor@#1,\gr@nil
}

\def\gr@OnlyFor@#1,{%
    \def\gr@temp{#1}%
    \def\next{\@ifnextchar\gr@nil{\@gobbletwo}{\gr@OnlyFor@}}%
    \ifx\gr@temp\Instance
        \let\next\gr@OnlyFor@dobody 
    \fi
    \next
}

\def\GetKeyValue#1{\csname gr-arg-/generic-reusable/#1\endcsname}

\def\gr@OnlyFor@dobody#1\gr@nil{\@firstofone}

\def\InsertModule#1#2{% 
    \@ifnextchar[{\gr@InsertModule@{#1}{#2}}{\gr@InsertModule@{#1}{#2}[]}%
}
\def\gr@InsertModule@#1#2[#3]{%
    \pgfqkeys{/generic-reusable}{#3}
    \def\Instance{#2}%
    \def\gr@filename{#1}%
    \input #1 \relax
    \gr@markincluded{#1}
}
\makeatother

\begin{document}
\InsertModule{generic-reusable-file}{instance-A}[key1=somevalue1,key2=somevalue2]

\vskip10pt

\InsertModule{generic-reusable-file}{instance-B}

\vskip10pt

\InsertModule{generic-reusable-file}{instance-C}[some other key=some value]
\end{document}

输出为:

instance-A
only once code somevalue1 somevalue2
every time code
’instance-A’ code.

instance-B
every time code
’instance-B’ or ’instance-C’ code

instance-C
every time code
some value ’instance-B’ or ’instance-C’ code

相关内容