如何定义变量\var{arg1: Name}{arg2: Family}
并在文本中使用类似
我的名字是
\var$arg1
,我的家人是\var$arg2
arg1
和arg2
是参数的标签。
答案1
尽管你的想法与 TeX 中使用的典型语法相差甚远,但这是可能的。\var$arg1
这里只通过(添加分号)更改了使用语法\var$arg1;
,因为我们需要知道参数名称的终止位置。
\def\var{\futurelet\next\varA}
\def\varA{\let\varB=\relax
\ifx\next$\let\varB=\varC\fi %$
\ifx\next\bgroup\let\varB=\varD\fi
\varB
}
\def\varC$#1;{\csname var:#1\endcsname} %$
\def\varD#1{\varE#1\end\var}
\def\varE#1: #2\end{\expandafter\def\csname var:#1\endcsname{#2}}
\var{arg1: Name}{arg2: Family}
My name is \var$arg1; and my family is \var$arg2;.
\bye
答案2
您可以定义一个属性列表,并向其中添加名称以及用于检索它们的键。
\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\prop_new:N \g_amin_names_prop
\NewDocumentCommand{\defineperson}{mmm}
{
\prop_gput:Nnn \g_amin_names_prop { #1-name } { #2 }
\prop_gput:Nnn \g_amin_names_prop { #1-family } { #3 }
}
\NewDocumentCommand{\personname}{m}
{
\prop_item:Nn \g_amin_names_prop { #1-name }
}
\NewDocumentCommand{\personfamily}{m}
{
\prop_item:Nn \g_amin_names_prop { #1-family }
}
\ExplSyntaxOff
\defineperson{amin}{Amin}{Roshani}
\defineperson{blu}{Ben}{User}
\begin{document}
My name is \personname{amin}, my family is \personfamily{amin}.
My best friend is \personname{blu} \personfamily{blu}.
\end{document}
答案3
几个月前我写了一个\SetProperty
--\GetProperty
界面。
这个概念是关于维护扩展为 2 元组参数列表的宏。
此类元组的第一个组件保存属性的名称。第二个组件保存属性的值。
维护这些宏的“用户界面”由两个控制序列组成:
\SetProperty{\macro}{<name of property>}{<new value of property>}
\GetProperty{\macro}{<name of property>}{<tokens to be delivered in case property is not available>}
例如,
\SetProperty{\foobar}{Property A}{Property A's value}
\SetProperty{\foobar}{Property B}{Property B's value}
将使宏\foobar
扩展为
{{Property A}{Property A's value}}% <- 1st 2-tuple
{{Property B}{Property B's value}}% <- 2nd 2-tuple
例如,
\GetProperty{\foobar}{Property A}{Property A not available in \texttt{\string\foobar}}
将扩展为:
Property A's value
例如,
\GetProperty{\foobar}{Property C}{Property C not available in \texttt{\string\foobar}}
将扩展为:
Property C not available in \texttt{\string\foobar}
但属性 C 将在以下情况下可用:
\SetProperty{\foobar}{Property C}{Property C's value}
,因此说完之后,\foobar
会扩展为
{{Property A}{Property A's value}}% <- 1st 2-tuple
{{Property B}{Property B's value}}% <- 2nd 2-tuple
{{Property C}{Property C's value}}% <- 3rd 2-tuple
和
\GetProperty{\foobar}{Property C}{Property C not available in \texttt{\string\foobar}}
则结果为:
Property C's value
更详细的解释位于下面包含整个接口编码的示例的注释中:
%% This coding example
%% - was written by Ulrich Diez in December 14, 2016
%% - was modified by Ulrich Diez in April 07, 2017
%% - Copyright 2016, 2017 Ulrich Diez
%% (e-mail: [email protected])
%%
%% This work may be distributed and/or modified under the conditions of
%% the LaTeX Project Public License, either version 1.3c of this license
%% or (at your option) any later version.
%%
%% The latest version of this license is in
%%
%% <http://www.latex-project.org/lppl.txt>
%%
%% and version 1.3x or later is part of all distributions of LaTeX
%% version 2005/12/01 or later.
%%
%% This work has the LPPL maintenance status `unmaintained'.
%%
%% This work consists of this coding example.
%%
\errorcontextlines=10000
\documentclass{article}
\makeatletter
%%==== Begin of code for the \SetProperty-\GetProperty-Interface =======
\RequirePackage{ifluatex, ifxetex}
%%======================================================================
%% Paraphernalia:
%% \UD@firstoftwo, \UD@secondoftwo, \UD@PassFirstToSecond,
%% \UD@exchange, \UD@removespace, \UD@name, \UD@CheckWhetherNull,
%% \UD@loopcall,
%%......................................................................
\newcommand\UD@firstoftwo[2]{#1}%
\newcommand\UD@secondoftwo[2]{#2}%
\newcommand\UD@PassFirstToSecond[2]{#2{#1}}%
\newcommand\UD@exchange[2]{#2#1}%
\newcommand\UD@removespace{}\UD@firstoftwo{\def\UD@removespace}{} {}%
%%----------------------------------------------------------------------
%% Put a control sequence token in place instead of the string denoting
%% its name.
%%......................................................................
%% \UD@name<emptiness or tokens other than braces>{<Name of
%% Control Sequence>}
%%
%% yields:
%%
%% <emptiness or tokens other than braces>\Controlsequence
%%
%% E.g.,
%%
%% \UD@name foo{bar} -> foo\bar
%% \UD@name{bar} -> \bar
%% \UD@name\newcommand*{wEirdName}[1]{Arg 1: (#1)}
%% -> \newcommand*\wEirdName[1]{Arg 1: (#1)}
%%
\newcommand\UD@name{}\long\def\UD@name#1#{\romannumeral\UD@@name{#1}}%
\newcommand\UD@@name[2]{%
\expandafter\UD@exchange\expandafter{\csname#2\endcsname}{0 #1}%
}%
%%----------------------------------------------------------------------
%% Check whether argument is empty:
%%......................................................................
%% \UD@CheckWhetherNull{<Argument which is to be checked>}%
%% {<Tokens to be delivered in case that argument
%% which is to be checked is empty>}%
%% {<Tokens to be delivered in case that argument
%% which is to be checked is not empty>}%
%%
%% The gist of this macro comes from Robert R. Schneck's \ifempty-macro:
%% <https://groups.google.com/forum/#!original/comp.text.tex/kuOEIQIrElc/lUg37FmhA74J>
%%
%% (\romannumeral expansion was introduced in order to overcome the
%% concerns and worries about improperly balanced
%% \if..\else..\fi constructs.)
%%
\newcommand\UD@CheckWhetherNull[1]{%
\romannumeral0\expandafter\UD@secondoftwo\string{\expandafter
\UD@secondoftwo\expandafter{\expandafter{\string#1}\expandafter
\UD@secondoftwo\string}\expandafter\UD@firstoftwo\expandafter{%
\expandafter\UD@secondoftwo\string}\expandafter\expandafter
\UD@firstoftwo{ }{}\UD@secondoftwo}{\expandafter\expandafter
\UD@firstoftwo{ }{}\UD@firstoftwo}%
}%
%%----------------------------------------------------------------------
%% Expandable Loop:
%% \UD@loopcall{<action>}%
%% {<action if list empty>}%
%% {<preset>}%
%% {{<e_k>}{<e_(k+1)>}..{e_n}}% <- this is the list
%%
%% If list is empty: <action if list empty>
%% Else:
%% <action>{<e_k>}<preset> \UD@loopcall{<action>}%
%% {<action if list empty>}%
%% {<preset>}{{<e_(k+1)>}..{e_n}}
%%
%% <action> can be defined to mesh into the iteration-process, e.g.,
%% (ex)changing arguments like the <action if list empty>-argument for
%% the next \UD@loopcall-iteration, e.g., terminating iteration
%% prematurely under some circumstances.
%%......................................................................
\newcommand\UD@RemoveTillUD@nil{}%
\long\def\UD@RemoveTillUD@nil#1#2\UD@nil{{#1}}%
\newcommand\UD@Extractfirstloop[1]{%
\expandafter\UD@CheckWhetherNull\expandafter{\UD@firstoftwo{}#1}%
{\UD@exchange{#1}}%
{%
\expandafter\UD@Extractfirstloop
\expandafter{\UD@RemoveTillUD@nil#1}%
}%
}%
\newcommand\UD@loopcall[4]{%
\UD@CheckWhetherNull{#4}{#2}{%
\expandafter\UD@exchange
\expandafter{\expandafter{\UD@firstoftwo{}#4}}%
{\UD@Extractfirstloop{#4\UD@nil}{#1}#3\UD@loopcall{#1}{#2}{#3}}%
}%
}%
%%======================================================================
%% Expandable comparison of two strings:
%%
%% Derived from David Kastrup's \ifstrequal-test from the
%% TeX Pearl Diving Site;
%% Pearls of 2005;
%% Title: David Kastrup - Comparing two strings known to consist
%% only of characters ;
%% Url: <http://www.gust.org.pl/projects/pearls/2005p/david-kastrup/bachotex2005-david-kastrup-pearl1.pdf>
%%......................................................................
%% \\UD@ifstrequal{<String 1>}{<String 2>}%
%% {%
%% <Tokens to be delivered in case strings are equal>
%% }%
%% {%
%% <Tokens to be delivered in case strings are
%% not equal>
%% }%
%%
%% (<String 1> gets expanded during comparison.
%% <String 2> gets not expanded during comparison.
%%
%% I bloated this thing up for ensuring it also takes spaces into
%% account.
%%
\@ifdefinable\UD@strchksp{%
\long\def\UD@strchksp#1#2#3#4 #5\relax{%
#1{\if\UD@strequal\UD@secondoftwo{}{#4}%
\expandafter\UD@firstoftwo\else\expandafter\UD@secondoftwo\fi
{\if\UD@strequal\UD@secondoftwo{}{#5}%
\expandafter\UD@firstoftwo\else\expandafter\UD@secondoftwo\fi
{\UD@strequalstart#1{#2}{#3} \relax}%
{\UD@strequalstart#1{#2}{#3}{ }#5\relax}}}%
{\UD@strequalstart#1{#2}{#3}#4 #5\relax}%
}%
}
\newcommand\UD@strequal[2]{\number\UD@strchksp#1{}{}#2 \relax}
\newcommand\UD@strequalstart[4]{%
\if#4\relax\UD@strequalstop\fi
\UD@strchksp#1{\if#4#2}{#3\fi}%
}%
\@ifdefinable\UD@strequalstop{%
\long\def\UD@strequalstop\fi\UD@strchksp#1#2#3#4{\fi#2#4\relax'#313 }%
}
\newcommand\UD@ifstrequal[2]{%
\romannumeral0%
\if\UD@strequal\@firstofone{#2.}{#1.}%
\expandafter\UD@firstoftwo\else\expandafter\UD@secondoftwo\fi
{\UD@exchange{ \UD@firstoftwo}}{\UD@exchange{ \UD@secondoftwo}}%
{\expandafter\expandafter\expandafter}%
}%
%%======================================================================
%% Total expansion and stringification of argument:
%%......................................................................
%% \UD@stringify{<argument that expands to character tokens>}
%%
%% Does \csname..\endcsname with the argument, then \string,
%% then removal of \escapechar if that was added.
%%
\newcommand\UD@stringify[1]{%
\romannumeral0%
\expandafter\expandafter\expandafter\UD@exchange
\expandafter\expandafter\expandafter{%
\expandafter\string\csname#1\endcsname}%
{%
\ifnum\the\escapechar<0 %
\expandafter\UD@secondoftwo\else\expandafter\UD@firstoftwo\fi
{%
\ifnum\the\escapechar>\ifxetex 1114111 %
\else\ifluatex 1114111 \else 255 \fi\fi
\expandafter\UD@secondoftwo\else\expandafter\UD@firstoftwo\fi
{%
\ifnum\the\escapechar=32 %
\expandafter\UD@secondoftwo\else\expandafter\UD@firstoftwo\fi
{\UD@firstoftwo{ }}{}%
}{ }%
}{ }%
}%
}%
%%======================================================================
%% Property-Management:
%%......................................................................
%%
%% The concept is about maintaining macros that expand to lists of
%% 2-tuple-arguments.
%% The first component of the tuple holds the name of a property.
%% The second component holds the value of the property.
%%
%% E.g., the macro \macro could be defined to expand to:
%%
%% {{property name 1}{property value 1}}%
%% {{property name 2}{property value 2}}%
%% ...
%% {{property name K}{property value K}}%
%%
%%
%% \SetProperty{\macro}%
%% {<name of property>}%
%% {<new value of property>}
%%
%% In case \macro is undefined, \macro will be defined empty.
%%
%% In case a property <name of property> does not exist within the
%% macro \macro, it will be added to the macro and it will get the
%% value <new value of property>.
%%
%% In case a property <name of property> does exist within the macro
%% \macro, its value will get replaced by <new value of property>.
%%
%% Before further evaluation <name of property> will be expanded via
%% \csname..\endcsname-expansion and afterwards "stringified" by
%% applying \string and removing a leading escapechar if one was
%% added.
%%
%% Example:
%%
%% \SetProperty{\macro}{property name 2}{changed property value 2}
%%
%% would make \macro to expand to
%%
%% {{property name 1}{property value 1}}%
%% {{property name 2}{changed property value 2}}%
%% ...
%% {{property name K}{property value K}}%
%%
%% and
%%
%% \SetProperty{\macro}{property name (K+1)}{property value (K+1)}
%%
%% would make \macro to expand to
%%
%% {{property name 1}{property value 1}}%
%% {{property name 2}{changed property value 2}}%
%% ...
%% {{property name K}{property value K}}%
%% {{property name (K+1)}{property value (K+1)}}%
%%
%%
%% \GetProperty{\macro}%
%% {<name of property>}%
%% {%
%% <tokens to be delivered in case
%% property is not available>
%% }
%%
%% In case \macro is undefined or property <name of property> does not
%% exist within the macro \macro,
%% <tokens to be delivered in case property is not available>
%% will be delivered.
%%
%% In case a property <name of property> does exist within the macro
%% \macro, its value will be delivered.
%%
%% Before further evaluation <name of property> will be expanded via
%% \csname..\endcsname-expansion and afterwards "stringified" by
%% applying \string and removing a leading escapechar if one was
%% added.
%%
%% \GetProperty is expandable and delivers the result after two
%% expansion steps / after being hit "twice" by \expandafter.
%%
%% E.g., with the macro \macro being defined to expand to
%%
%% {{property name 1}{property value 1}}%
%% {{property name 2}{property value 2}}%
%% ...
%% {{property name K}{property value K}}%
%%
%% , the sequence
%%
%% \GetProperty{\macro}{property name 2}{Huh?}
%%
%% will expand to:
%%
%% property value 2
%%
\newcommand\UD@ExpandProperties[3]{%
\expandafter\UD@PassFirstToSecond\expandafter{#1}%
{\expandafter\expandafter\expandafter\UD@PassFirstToSecond
\expandafter\expandafter\expandafter{%
\UD@stringify{#2}}{#3}}%
}%
\newcommand\UD@AtIfPropertyListUndefined[1]{%
\@ifundefined{%
\expandafter\UD@exchange\expandafter{\string#1}%
{%
\ifnum\the\escapechar<0 %
\expandafter\UD@secondoftwo\else\expandafter\UD@firstoftwo\fi
{%
\ifnum\the\escapechar>\ifxetex 1114111 %
\else\ifluatex 1114111 \else 255 \fi\fi
\expandafter\UD@secondoftwo\else\expandafter\UD@firstoftwo\fi
{%
\ifnum\the\escapechar=32 %
\expandafter\UD@secondoftwo\else\expandafter\UD@firstoftwo\fi
{\UD@firstoftwo{}}{\UD@removespace}%
}{}%
}{}%
}%
}
}%
\newcommand\SetProperty[2]{%
\@bsphack
\UD@AtIfPropertyListUndefined{#1}{\newcommand*#1{}}{}%
\romannumeral0%
\UD@ExpandProperties{#1}{#2}{\UD@@setproperty}{#1}%
}%
\newcommand\UD@@setproperty[4]{%
\UD@loopcall{\UD@@@setproperty}%
{ \def#3{{{#1}{#4}}}\@esphack}%
{{#1}{#4}{ \def#3}{}}%
{#2}%
}%
\newcommand\UD@@@setproperty{}%
\long\def\UD@@@setproperty#1#2#3#4#5\UD@loopcall#6#7#8#9{%
\UD@ifstrequal{\UD@firstoftwo#1}{#2}%
{%
#4{#5{{#2}{#3}}#9}\@esphack
}{%
\UD@loopcall{#6}%
{#4{#5{#1}{{#2}{#3}}}\@esphack}%
{{#2}{#3}{#4}{#5{#1}}}%
{#9}%
}%
}%
\newcommand\GetProperty[3]{%
\romannumeral0%
\UD@AtIfPropertyListUndefined{#1}{ #3}{%
\UD@ExpandProperties{#1}{#2}{\UD@@getproperty}{ #3}%
}%
}%
\newcommand\UD@@getproperty[3]{%
\UD@loopcall{\UD@@@getproperty}%
{#3}%
{{#1}}%
{#2}%
}%
\newcommand\UD@@@getproperty{}%
\long\def\UD@@@getproperty#1#2\UD@loopcall#3#4#5#6{%
\UD@ifstrequal{\UD@firstoftwo#1}{#2}%
{\UD@exchange{ }\expandafter\UD@secondoftwo#1}%
{%
\UD@loopcall{#3}%
{#4}%
{#5}%
{#6}%
}%
}%
%%==== End of code for the \SetProperty-\GetProperty-Interface =========
\makeatother
%%==== Layout of this example ==========================================
% - No headers / no footers
\pagestyle{empty}
% - horizontal margins:
\parindent=0ex
\setlength\textwidth{\paperwidth}
\addtolength\textwidth{-3cm}
\setlength\oddsidemargin{1.5cm}%
\addtolength\oddsidemargin{-1in}%
\addtolength\oddsidemargin{-\hoffset}%
\setlength\evensidemargin{\oddsidemargin}%
\setlength\marginparwidth{1.5cm}%
\addtolength\marginparwidth{-2\marginparsep}%
% - vertical margins:
\setlength\topmargin{1.5cm}
\addtolength\topmargin{-1in}
\addtolength\topmargin{-\voffset}
% - no headheight/headsep etc as there are no headers as
% pagestyle=empty:
\setlength\headsep{0pt}
\setlength\headheight{0pt}
\setlength\footskip{0pt}
\setlength\textheight{\paperheight}
\addtolength\textheight{-3cm}%
\addtolength\textheight{-\footskip}%
\addtolength\textheight{-\headsep}%
\addtolength\textheight{-\headheight}%
% - allow linebreaks after closing braces in typewriter font:
\DeclareFontFamily{\encodingdefault}{\ttdefault}{\hyphenchar\font=`\}}
% - in case of pdftex also adjust the underlying paper
\expandafter\ifx\csname pdfpagewidth\endcsname\relax\else
\pdfpagewidth=\paperwidth
\pdfpageheight=\paperheight
\fi
%%==== Layout-changes etc done.=========================================
\begin{document}
\sloppy
\verb|\SetProperty{\myNicePropertyList}{name}{Mme Xample}|:\\
\SetProperty{\myNicePropertyList}{name}{Mme Xample}
\texttt{\string\myNicePropertyList: \meaning\myNicePropertyList}
\bigskip
\verb|\SetProperty{\myNicePropertyList}{position}{Manager}:|\\
\SetProperty{\myNicePropertyList}{position}{Manager}
\texttt{\string\myNicePropertyList: \meaning\myNicePropertyList}
\bigskip
\verb|\SetProperty{\myNicePropertyList}{company}{Example Company}|:\\
\SetProperty{\myNicePropertyList}{company}{Example Company}
\texttt{\string\myNicePropertyList: \meaning\myNicePropertyList}
\bigskip
\verb|\SetProperty{\myNicePropertyList}{street}{11 Xample Street}|:\\
\SetProperty{\myNicePropertyList}{street}{11 Xample Street}
\texttt{\string\myNicePropertyList: \meaning\myNicePropertyList}
\bigskip
\verb|\SetProperty{\myNicePropertyList}{location}{Xample Ville}|:\\
\SetProperty{\myNicePropertyList}{location}{Xample Ville}
\texttt{\string\myNicePropertyList: \meaning\myNicePropertyList}
\bigskip
\verb|\SetProperty{\myNicePropertyList}{postal code}{SW1E 6LB}|:\\
\SetProperty{\myNicePropertyList}{postal code}{SW1E 6LB}
\texttt{\string\myNicePropertyList: \meaning\myNicePropertyList}
\bigskip
\verb|\SetProperty{\myNicePropertyList}{country}{EXAMPLISTAN}|:\\
\SetProperty{\myNicePropertyList}{country}{EXAMPLISTAN}
\texttt{\string\myNicePropertyList: \meaning\myNicePropertyList}
\vfill
\verb|\GetProperty{\myNicePropertyList}{name}{DATA NOT AVAILABLE}|:
\GetProperty{\myNicePropertyList}{name}{DATA NOT AVAILABLE}
\smallskip
\verb|\GetProperty{\myNicePropertyList}{position}{DATA NOT AVAILABLE}|:
\GetProperty{\myNicePropertyList}{position}{DATA NOT AVAILABLE}
\smallskip
\verb|\GetProperty{\myNicePropertyList}{company}{DATA NOT AVAILABLE}|:
\GetProperty{\myNicePropertyList}{company}{DATA NOT AVAILABLE}
\smallskip
\verb|\GetProperty{\myNicePropertyList}{street}{DATA NOT AVAILABLE}|:
\GetProperty{\myNicePropertyList}{street}{DATA NOT AVAILABLE}
\smallskip
\verb|\GetProperty{\myNicePropertyList}{location}{DATA NOT AVAILABLE}|:
\GetProperty{\myNicePropertyList}{location}{DATA NOT AVAILABLE}
\smallskip
\verb|\GetProperty{\myNicePropertyList}{postal code}{DATA NOT AVAILABLE}|:
\GetProperty{\myNicePropertyList}{postal code}{DATA NOT AVAILABLE}
\smallskip
\verb|\GetProperty{\myNicePropertyList}{country}{DATA NOT AVAILABLE}|:
\GetProperty{\myNicePropertyList}{country}{DATA NOT AVAILABLE}
\smallskip
\verb|\GetProperty{\myNicePropertyList}{an undefined property}{DATA NOT AVAILABLE}:|
\GetProperty{\myNicePropertyList}{an undefined property}%
{DATA NOT AVAILABLE}
\smallskip
\verb|\GetProperty{\UndefinedPropertyList}{name}{DATA NOT AVAILABLE}:|
\GetProperty{\UndefinedPropertyList}{name}{DATA NOT AVAILABLE}
\bigskip%\newpage
Now change some things:
\bigskip
\verb|\SetProperty{\myNicePropertyList}{street}{11 Xample Lane}|:\\
\SetProperty{\myNicePropertyList}{street}{11 Xample Lane}
\texttt{\string\myNicePropertyList: \meaning\myNicePropertyList}
\bigskip
\verb|\SetProperty{\myNicePropertyList}{location}{Xample Junction}|:\\
\SetProperty{\myNicePropertyList}{location}{Xample Junction}
\texttt{\string\myNicePropertyList: \meaning\myNicePropertyList}
\bigskip
\verb|\SetProperty{\myNicePropertyList}{postal code}{NE2F 8KC}|:\\
\SetProperty{\myNicePropertyList}{postal code}{NE2F 8KC}
\texttt{\string\myNicePropertyList: \meaning\myNicePropertyList}
\vfill
\verb|\GetProperty{\myNicePropertyList}{street}{DATA NOT AVAILABLE}|:
\GetProperty{\myNicePropertyList}{street}{DATA NOT AVAILABLE}
\smallskip
\verb|\GetProperty{\myNicePropertyList}{location}{DATA NOT AVAILABLE}|:
\GetProperty{\myNicePropertyList}{location}{DATA NOT AVAILABLE}
\smallskip
\verb|\GetProperty{\myNicePropertyList}{postal code}{DATA NOT AVAILABLE}|:
\GetProperty{\myNicePropertyList}{postal code}{DATA NOT AVAILABLE}
\vfill
\end{document}