我经常发现自己对具有以下语义的 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