使用 newcommand 创建最终用户可编辑变量的建议方法

使用 newcommand 创建最终用户可编辑变量的建议方法

我正在尝试创建一个可供许多人使用的文档模板,其中最终用户可以控制某些值,但大多数模板样式可以集中控制。想象一个(简化的)目录结构,如下所示:

.
├── latex
│   ├── front_pages.tex
│   ├── packages.tex
│   └── preamble.tex
├── Makefile
├── src
│   └── user_config.tex
├── sty
│   └── department_thesis.sty
└── user_thesis.tex

这个想法是让模板具有不同的最终用户可编辑文件(user_config.tex),其余的是集中控制的“模板”文件(整个内容将用作 Git 子模块),最终用户永远不必触碰这些文件。如果我希望最终用户能够通过输入来控制某些内容,那么是否user_config.tex有一种“最佳”或最“LaTeX-ic”的方法来实现这一点,即使用 创建最终用户可访问的变量\newcommand

例如,如果我想定义一个命令,department_thesis.sty最终用户将使用它作为“变量”,user_config.tex然后该命令将真正被使用,front_pages.tex人们对如何最好地做到这一点有什么建议?

会不会是类似

# department_thesis.sty
\newcommand\AuthorFirstName[1]{\newcommand{\authorfirstname}{#1}}

# user_config.tex
\AuthorFirstName{Graduate} % End user puts in their name here

# front_pages.tex
...
\authorfirstname % Here it is actually being used
...

或者以 setter/getter 的思维方式思考是否更好?

# department_thesis.sty
\newcommand\SetAuthorFirstName[1]{\newcommand{\AuthorFirstName}{#1}}

# user_config.tex
\SetAuthorFirstName{Graduate} % End user puts in their name here

# front_pages.tex
...
\AuthorFirstName % Here it is actually being used
...

或者,这一切都不是思考让最终用户能够控制某些值同时又能够集中控制文档其余样式的问题的好方法?

答案1

我为我的handin-package. 答案中的代码根据编辑有所不同:

编辑1\@macroname@noerror现在当未设置默认值时不会出现错误。

编辑2:代码行为相同,但添加了注释并删除了一些重复内容以保持紧凑。还添加了生成的宏的示例用例\ifset@

我怀疑将命令名称打印到页面上可能是不想要的行为。要删除它,只需删除包含注释的行%<- Print command name to page.。此外:阅读注释。一切都解释得非常详细 :)

\documentclass{article}
\begin{document}
  %%% Usage:
  %%%\settable{text}
  %%% The text you enter would be a macro:
  %%%: \settable{hello}
  %%%:  if now \@hello is called,
  %%%:  a warning is displayed with
  %%%:  the text "\hello not set"
  %%%: \hello{world}
  %%%: if now \@hello is called, it prints "world"
  %%%: \@hello@noerror gives the returning
  %%%: content and empty without error if no content set.
  \makeatletter
  \let\ea = \expandafter
  \newcommand{\settable}[2][\@nil]{
    %%% example call \settable[actionIfNotSet(default action)]{macroName}
    %%% i.e. #1: is what to do if called and not set and
    %%% #2 is the macroname
    %
    %------ Example case ------%
    % The comments assume that someone called
    % \settable[world]{hello}
    %------ Code ------%
    %
    % First define the \hello command. The code below
    % is equivalent to
    % \def\hello##1{
    %   \def\@hello{##1}
    %   \def\@hello@noerror{##1}
    % }
    \ea\def\csname #2\ea\endcsname##1{
      \ea\def\csname @#2\endcsname{##1}
      \ea\def\csname @#2@noerror\endcsname{##1}
      \ea\def\csname isset@#2\endcsname{1}%<- Used by \ifset@hello. Explained below:
    }
    % Now \hello is a callable macro.
    %
    % Next: create the command to check if \hello is defined.
    % usage: \ifset@hello{User has set the macro}{User has not set the macro}
    % Below is equivalent to
    % \def\ifset@hello##1##2{
    % \ifcsname isset@hello\endcsname% <- if the macro \isset@hello exists. \isset@hello is defined when \hello is called
    %   ##1%
    % \else
    %   ##2%
    % \fi
    % }
    \ea\def\csname ifset@#2\endcsname##1##2{
    \ifcsname isset@#2\endcsname%
      ##1%
    \else
      ##2%
    \fi
    }
    % Now, make a macro to hold the default value and
    % define it to be the given optional argument (\@nil by default, "world" in the
    % example). The code below is equivalent (in the example case)
    % to \def\default@hello{world}
    \ea\def\csname default@#2\endcsname{#1}%
    % Check if optional argument is given (e.g. if it is equal to \@nil)
    \ifx#1\@nil\relax
      % If no default value given, then set e.g. \@hello@noerror to be an empty value.
      % The very first version of this answer would not have the below line.
      \ea\def\csname @#2@noerror\endcsname{}%
      % Also, if the opt arg is not given we define the default behaviour,
      % \default@hello in the example case
      % to print a console warning, as well as printing the macro name to the page
      \ea\def\csname default@#2\endcsname{
        {\@latex@warning{\@backslashchar #2 not given}}% <- Console warning
        \textbackslash #2% <- Print command name to page.
      }
    \else
      % If default value given, then set the \@hello@noerror to that value.
      % Since "world" was given as optional in the example case, this will be
      % the same as \def\@hello@noerror{world}
      \ea\def\csname @#2@noerror\endcsname{#1}%
    \fi

    % We now define the usage macro \@hello:
    \ea\def\csname @#2\endcsname{
      \csname default@#2\endcsname
    }
  }
  \settable{hello}
  % A summary of generated macros
  \ifset@hello{     %<-  Prints "User did not set hello"
    User did set hello
  }{
    User did not set hello
  }
  \@hello@noerror   %<- Empty value. Does nothing extra by default.
  \@hello           %<- Gives warning message "\hello not given", and prints "\hello" on page.
  \hello{world}     %<- Sets \@hello and \@hello@noerror to "world"
  \ifset@hello{     %<- Prints "User did set hello"
    User did set hello
  }{
    User did not set hello
  }
  \@hello           %<- Prints "world"
  \@hello@noerror   %<- Prints "world"
\end{document}

相关内容