我不想使用以下内容
\begingroup
\edef\next{\endgroup
\noexpand\const{Xp}{\X(tp)}
\noexpand\const{Yp}{\Y(tp)}
\noexpand\const{Xf}{\X(tf)}
\noexpand\const{Yf}{\Y(tf)}
}\next
和
\begingroup
\edef\next{\endgroup
\noexpand\psparametricplot[algebraic,plotpoints=100]{0}{\tf}{\X(t)|\Y(t)}}\next
因为它们看上去很神秘。
相反,我想创建一个新的环境,如下所示,
\newenvironment{DelayExpansion}
{\begingroup
\edef\next\bgroup\endgroup\ignorespaces}
{\egroup\next\ignorespacesafterend}
但不幸的是,它会产生编译错误,您可以自己查看。
如果错误可以修复,则环境将按如下方式使用,
\DelayExpansion
\noexpand\const{Xp}{\X(tp)}
\noexpand\const{Yp}{\Y(tp)}
\noexpand\const{Xf}{\X(tf)}
\noexpand\const{Yf}{\Y(tf)}
\endDelayExpansion
和
\DelayExpansion
\noexpand\psparametricplot[algebraic,plotpoints=100]{0}{\tf}{\X(t)|\Y(t)}}
\endDelayExpansion
或者不指定,\noexpand
如下所示。
\DelayExpansion
\const{Xp}{\X(tp)}
\const{Yp}{\Y(tp)}
\const{Xf}{\X(tf)}
\const{Yf}{\Y(tf)}
\endDelayExpansion
和
\DelayExpansion
\psparametricplot[algebraic,plotpoints=100]{0}{\tf}{\X(t)|\Y(t)}}
\endDelayExpansion
真实场景的 MWE 如下所示。
\documentclass[pstricks,border=12pt]{standalone}
\usepackage{pst-plot}
\addtopsstyle{gridstyle}{gridlabels=0,griddots=0,subgriddiv=5,gridwidth=0.4pt,subgridwidth=0.2pt}
\usepackage[nomessages]{fp}
\newcommand\const[3][3]{%
\expandafter\FPeval\csname#2\endcsname{round(#3:#1)}%
\pstVerb{/#2 \csname#2\endcsname\space def}%
}
\const{Vox}{2}
\const{Xo}{1}
\const{Voy}{4}
\const{Yo}{10}
\const{g}{10}
\def\X(#1){Xo+Vox*#1}
\def\Y(#1){Yo+Voy*#1-0.5*g*#1^2}
\const{tp}{Voy/g}
\const{toffset}{0.5}
\const{tf}{2*tp+toffset}
%\newenvironment{DelayExpansion}
%{\begingroup
%\edef\next\bgroup\endgroup\ignorespaces}
%{\egroup\next\ignorespacesafterend}
%
%\DelayExpansion
%\noexpand\const{Xp}{\X(tp)}
%\noexpand\const{Yp}{\Y(tp)}
%\noexpand\const{Xf}{\X(tf)}
%\noexpand\const{Yf}{\Y(tf)}
%\endDelayExpansion
\begingroup
\edef\next{\endgroup
\noexpand\const{Xp}{\X(tp)}
\noexpand\const{Yp}{\Y(tp)}
\noexpand\const{Xf}{\X(tf)}
\noexpand\const{Yf}{\Y(tf)}
}\next
\begin{document}
\begin{pspicture}[showgrid](0,\Yp)(\Xf,\Yf)
\psframe[fillstyle=vlines,fillcolor=gray](0,\Yf)(\Xo,\Yo)
\psparametricplot[algebraic,plotpoints=100]{0}{\tf}{\X(t)|\Y(t)}
\end{pspicture}
\end{document}
如何打造“延迟扩展”环境?
最新更新:
我尝试了 Ryan 给出的解决方案,但仍然会产生一些错误(请自行尝试查看错误信息)。
\documentclass[pstricks,border=12pt]{standalone}
\usepackage{pst-plot}
\usepackage[nomessages]{fp}
% cannot be simplified unless I must break my real scenario
\newcommand\const[3][3]{%
\expandafter\FPeval\csname#2\endcsname{round(#3:#1)}%
\pstVerb{/#2 \csname#2\endcsname\space def}%
}
\const{Xo}{1}
\const{Yo}{10}
\const{tp}{0.4}
\const{tf}{2*tp+1.5}
\def\X(#1){Xo+2*#1}
\def\Y(#1){Yo+4*#1-0.5*5*#1^2}
% try this for comparison first
%\begingroup
%\edef\next{\endgroup
%\noexpand\const{Yp}{\Y(tp)}
%}\next
% Ryan's suggestin
\usepackage{environ}
\NewEnviron{DelayExpansion}{%
\begingroup
\edef\next{\endgroup\BODY}%
\next%
}
% Ryan's suggestion
\begin{DelayExpansion}
\noexpand\const{Yp}{\Y(tp)}
\end{DelayExpansion}
\begin{document}
\begin{pspicture}(0,6)(6,\Yp)
\psparametricplot[algebraic,plotpoints=100]{0}{\tf}{\X(t)|\Y(t)}
\end{pspicture}
\end{document}
我将为一个被接受的答案提供 4 个赏金,每个 500 美元。听起来是不是很兴奋?
答案1
这个environ
包就是为此而存在的。尝试一下:
\usepackage{environ}
\NewEnviron{DelayExpansion}{%
\global\let\xBODY\BODY
}[\aftergroup\expandBODY]
\def\expandBODY{%
\begingroup
\edef\next{\endgroup\xBODY}%
\next
}
它在应用之前收集整个环境\edef
。
您尝试失败的原因是\edef
(和\def
,和\xdef
) 不仅需要隐式括号 (例如 ) 来\bgroup
设置其内容,还需要实际括号{
。拆分\def
,这是确切地为什么environ
存在收集两个“开始”和“结束”宏之间包含的环境的内容。
\noexpand
我还应该说,如果不在所需位置明确指定,您不太可能编写出此环境的一个版本。如果您只想要\noexpand
行的开头,您可以尝试^^M
在环境之前激活并让它插入\noexpand
自身,但这听起来非常冒险……
编辑:导致错误的原因是您的宏\const
在内部通过进行定义\def
,但environ
包运行其代码里面环境,它受组保护(根据环境设计)。通常,您只需使用\gdef
或\xdef
来规避这种情况,但由于这样做会默认一个人的编码环境的特性,因此更自动化的方法是使用简单地将\edef
组导出到组之外\aftergroup
。我分两个阶段声明了此导出。首先,我\BODY
通过导出自身\global\let\BODY\BODY
,然后使用可选参数\NewEnviron
(在 处运行其内容\end{DelayExpansion}
)导出扩展代码。
这不需要任何技巧(除了\aftergroup
,这非常棘手),也不需要破解的特定代码\end
(如\end{DelayExpansion}
,除了知道其中有一个组)。
编辑2:这是一个稍微复杂一点的环境,模仿 egreg 的答案,可以自动保留指定的宏。我将在一个最小的情况下给出一个例子:
\documentclass{article}
\usepackage{environ,etoolbox}
\NewEnviron{DelayExpansion}[1][]{%
\global\let\xBODY\BODY
\global\def\xARGS{{#1}}
}[\aftergroup\expandBODY]
\def\expandBODY{%
\begingroup
\def\do##1{\let##1\relax}%
\expandafter\docsvlist\xARGS
\edef\next{\endgroup\xBODY}%
\next
}
\begin{document}
\begin{DelayExpansion}[\macro]
\def\macro{a}
\end{DelayExpansion}
\macro
\end{document}
(输出:a
)
或者,在你的例子中,
\begin{DelayExpansion}[\const]
\const{Yp}{\Y(tp)}
\end{DelayExpansion}
可选参数中命名的任何宏都不会扩展(列表以逗号分隔并且可能为空,在这种情况下环境不执行任何操作)。
它不使用 LaTeX3,而是使用etoolbox
,它为 LaTeX2e 提供了一些相同的编程便利。
答案2
我不会使用在团队中工作存在问题的环境。以下是建议:
\documentclass[pstricks,border=12pt]{standalone}
\usepackage{pst-plot}
\usepackage{xparse}
\usepackage[nomessages]{fp}
\newcommand\const[3][3]{%
\expandafter\FPeval\csname#2\endcsname{round(#3:#1)}%
\pstVerb{/#2 \csname#2\endcsname\space def}%
}
\ExplSyntaxOn
\NewDocumentCommand{\DelayExpansion}{mm}
{
\group_begin:
\clist_map_inline:nn { #1 } { \cs_set_eq:NN ##1 \scan_stop: }
\use:x { \group_end: #2 }
}
\ExplSyntaxOff
\const{Vox}{2}
\const{Xo}{1}
\const{Voy}{4}
\const{Yo}{10}
\const{g}{10}
\def\X(#1){Xo+Vox*#1}
\def\Y(#1){Yo+Voy*#1-0.5*g*#1^2}
\const{tp}{Voy/g}
\const{toffset}{0.5}
\const{tf}{2*tp+toffset}
\begin{document}
\DelayExpansion{\const}
{
\const{Xp}{\X(tp)}
\const{Yp}{\Y(tp)}
\const{Xf}{\X(tf)}
\const{Yf}{\Y(tf)}
}
\begin{pspicture}[showgrid](0,\Yp)(\Xf,\Yf)
\psparametricplot[algebraic,plotpoints=100]{0}{\tp}{\X(t)|\Y(t)}
\end{pspicture}
\end{document}
有\DelayExpansion
两个参数;第一个是不应扩展的宏列表,第二个参数是将要扩展的内容,除了列表中的宏之外。
在示例中,您必须使用两次\DelayExpansion
,因为宏\Yp
、\Xf
和\Yf
只有在被定义(由第一个\DelayExpansion
)后才可用。
怎么\DelayExpansion
運作?
首先,它使(在一个组中)所有选定的宏等效于\relax
(这是\scan_stop:
LaTeX3 的说法)。然后,第二个参数作为参数给出,\use:x
该参数完全展开所有内容(但\scan_stop:
不可扩展,因此第一个参数\const
将保留其自身)。
这只是一个众所周知的技巧的包装
\begingroup\edef\x{\endgroup<tokens>}\x
Ryan 也使用了它。这\group_end:
将使\const
(或第一个参数中的任何宏) 返回其先前的值,并且 LaTeX 将继续其正常操作。
答案3
构建于Ryan 的方法,可以通过一些扩展技巧将定义移出组:
\documentclass[pstricks,border=12pt]{standalone}
\usepackage{pst-plot}
\usepackage[nomessages]{fp}
% cannot be simplified unless I must break my real scenario
\newcommand{\const}[3][3]{%
\expandafter\FPeval\csname#2\endcsname{round(#3:#1)}%
\pstVerb{/#2 \csname#2\endcsname\space def}%
}
\const{Xo}{1}
\const{Yo}{10}
\const{tp}{0.4}
\const{tf}{2*tp+1.5}
\def\X(#1){Xo+2*#1}
\def\Y(#1){Yo+4*#1-0.5*5*#1^2}
\usepackage{environ}
\makeatletter
\NewEnviron{DelayExpansion}{%
\expandafter\endgroup
\expandafter\begingroup
\expandafter\edef\expandafter\next\expandafter
{\expandafter\endgroup\BODY}%
\next
\begingroup
\def\@currenvir{DelayExpansion}%
}
\makeatother
\begin{DelayExpansion}
\noexpand\const{Yp}{\Y(tp)}
\end{DelayExpansion}
\begin{document}
\begin{pspicture}(0,6)(6,\Yp)
\begin{DelayExpansion}
\noexpand\psparametricplot[algebraic,plotpoints=100]{0}{\tf}{\X(t)|\Y(t)}
\end{DelayExpansion}
\end{pspicture}
\end{document}
答案4
以下是 Ryan 的解决方案的一个版本,无需电子工具箱包。\dolist
可以被 LaTeX 的 替换\@for
。
\documentclass{article}
\usepackage{environ}
\begingroup
\catcode`\,=\active
\gdef\dolist#1#2{%
\begingroup\toks0{}%
\def,##1{\toks0\expandafter{\the\toks0 #2}}%
\catcode`\,=\active
\scantokens{,#1}%
\expandafter\endgroup\the\toks0 %
}
\endgroup
\NewEnviron{DelayExpansion}[1][]{%
\global\let\xBODY\BODY
\gdef\xARGS{#1}%
}[\aftergroup\expandBODY]
\def\expandBODY{%
\begingroup
\ifx\xARGS\empty\else
\expandafter\dolist\expandafter{\xARGS}{\let##1\relax}%
\fi
\edef\next{\endgroup\xBODY}\next
}
\begin{document}
\def\xmacro{a}
\begin{DelayExpansion}[\macro,\foo]
\def\foo##1{##1}%
\def\macro{\xmacro}
x
\end{DelayExpansion}
\macro
\end{document}