我正在尝试创建一个环境,其中文本仅在可选参数未更改时出现(而当参数更改时消失)。我试图做的要点是:
\newenvironment{entry}[3][0]
{\ifcase #1
\textbf{#2} \hfill {\textit{#3}}\\
\noindent
}
{\fi}
因此改变#1
切换两个都 #2
/#3
和我在环境中实际输入的任何文本。如果我将文本放在\fi
第一组括号内(在 之后\noindent
),它会触发#2
和#3
切换,但不会触发实际文本。但是,移至\fi
第二组括号会出现“ifcase 不完整”错误。有没有办法稍微修改代码以产生我想要的效果?
我正在尝试创建一个稍微动态的文档,通过更改几个快速值,我可以让整个部分显示或不显示。如果您对如何更好地做到这一点有任何建议,请随时告诉我。
PS:如果有人可以向我推荐一个全面介绍条件句的资料来源,我将不胜感激。
答案1
怎么样
\documentclass{article}
\usepackage{environ}
\NewEnviron{entry}[3][0]{\ifnum#1=0
\textbf{#2} \hfill {\textit{#3}}\\
\noindent\BODY\fi}
\begin{document}
\begin{entry}{a}{b}
Blub
\end{entry}
\begin{entry}[1]{c}{d}
Pft
\end{entry}
\end{document}
答案2
分析
让我解释一下你的代码有什么问题。
当输入流中的下一个标记是条件时,TeX 会查找其后的适当测试,并决定是遵循真分支还是假分支。另一个分支将被跳过无需解释令牌,但要注意外层可能出现的条件,以便与正确的\else
或相匹配\fi
。
条件\ifcase
略有不同,请参见关于 \ifcase 语法的一个问题,但想法是完全一样的。
当你调用 时会发生什么\begin{entry}{Hello}{World}
?没有可选参数,因此(跳过有关该过程的一些细节),TeX 最终会看到
\ifcase0 \textbf{Hello} \hfill {\textit{World}}\\\noindent<other text>\end{entry}
好的,测试告诉 TeX 仅在第一个\or
(\else
,如果没有\or
出现)之后跳过文本,因此它开始处理标记。到达 后\end{entry}
,TeX 会执行其常规工作,包括\endentry
在本例中生成\fi
。好!没有出现\or
和\else
,因此没有什么可跳过的;\fi
根据规则,标记会消失。
怎么办\begin{entry}[1]{Hello World}
? 经过宏替换后,TeX 可以
\ifcase1 \textbf{Hello} \hfill {\textit{World}}\\\noindent<other text>\end{entry}<rest of the document>
这里的问题很明显:TeX 必须跳过文本,直到到达第一个\or
或\else
或\fi
在外层,因为它在跳过文本时不会进行宏扩展。并且不所需形式的标记。TeX 将跳至文档末尾并抱怨条件不完整。
解决方案
在评估条件之前,您必须先吸收文本。标准方法是使用environ
或xparse
。
后者更加强大并且更加友好。
\usepackage{xparse}
\NewDocumentEnvironment{entry}{O{0} m m +b}
{\ifnum#1=0 \textbf{#2}\hfill\textit{#3}\\*#4\fi}
{}
参数指定为
O{0}
:可选参数,默认值为 0m
:标准强制参数+b
:环境主体,允许空行
“结束部分”是空的,因为那里没有特殊的处理要做。
这样,就\fi
可以在外层看到。
与之environ
几乎相同的是:
\usepackage{environ}
\NewEnviron{entry}[3][0]{%
\ifnum#1=0 \textbf{#2}\hfill\textit{#3}\\*\BODY\fi
}
主要区别在于参数的语法与 for 相同,\newenvironment
并且环境的主体由 表示\BODY
。
我推荐前者。 解决方案environ
已在另一个答案中给出;请检查差异:
\ifcase
不是正确的使用条件;\ifnum
更容易;\noindent
是多余的;\\*
将不允许在此特定换行符后进行分页。
答案3
例如,按照我的评论,您可以这样做:
\documentclass{article}
\newsavebox{\mybox}
\newenvironment{entry}[3][0]
{%
\def\mycase{0}%
\def\myArg{#1}%
\ifx\myArg\mycase
\textbf{#2} \hfill {\textit{#3}}\\
\else\relax\fi
\savebox\mybox\bgroup\vbox\bgroup}
{\egroup\egroup\ifx\mycase\myArg\usebox{\mybox}\fi}
\begin{document}
Test
\begin{entry}{Some}{text}
My content will be shown
\end{entry}
\begin{entry}[2]{Some}{text}
My content will not be shown
\end{entry}
\end{document}
答案4
环境基本上(我不会告诉所有的细节和微妙之处)的工作原理如下:
和
\newenvironment{foo}[⟨args⟩][⟨optional⟩]%
{⟨tokens before environment body⟩}%
{⟨tokens after environment body⟩}
- 定义了一个宏
\foo
,其定义的参数文本依据并且其定义的参数文本由 形成。[⟨args⟩][⟨optional⟩]
⟨balanced text⟩
⟨tokens before environment body⟩
\endfoo
定义了一个不处理参数的宏,其定义⟨balanced text⟩
由 组成⟨tokens after environment body⟩
。
使用\begin{foo)
-macro \begin
processes{foo}
作为其参数,并且
- 开始一个新的组/一个新的本地范围,
- 重新定义
\@currenvir
扩展为当前环境的名称,即扩展为短语foo
, - 执行
\csname foo\endcsname
,即调用宏\foo
,宏依次处理参数并在此传递⟨tokens before environment body⟩
。
使用\end{foo}
-macro \end
processes{foo}
作为其参数,并且
- 即调用
\csname endfoo\endcsname
宏,然后宏\endfoo
又传递⟨tokens after environment body⟩
, - 检查的参数是否
\end
等于的展开\@currenvir
,如果不相等,则传递错误消息, - 结束本地范围/本地组,
- 在某些情况下会对环境之后出现的事物的间距做一些事情。
您的代码存在以下问题:
应该\fi
匹配的\ifcase
隐藏在宏的定义文本中,\endentry
而控制字标记\endentry
则要在执行 时构造\end{entry}
。
因此,当在处理\begin{entry}
并由此扩展宏之后\entry
,LaTeX 在标记流中搜索 的\ifcase
匹配项时\fi
,如果 -branch 中的条件\ifcase
为假(= 如果可选参数表示大于 0 的数字),它肯定不会“看到”它,因此 -branch 中的内容\ifcase
不会被扩展/执行。
我建议加载 verbatim-package,并在条件为假时使用其comment
-environment 的内部结构。
这种方法的缺陷是您不能嵌套entry
-environment。
这种方法的另一个缺陷是entry
-ennvironment 不能在宏参数中或宏定义的平衡文本中使用,在这些地方,事物在类别代码机制下被标记,不适合entry
通过 verbatim-package-code 吞噬 -ennvironment。
这种方法的另一个缺陷是,在包含 的行中\end{entry}
,短语\end{entry}
后面不能有其他东西。同样值得一提的是,使用这种方法时,只有在环境主体被打印或不被视为注释的情况下,才会执行环境主体内的
全局分配(例如 - 之类的东西) 。\stepcounter
\documentclass{article}
\usepackage{verbatim}
\newenvironment{entry}[3][0]{%
%\csname @\ifcase #1first\else second\fi oftwo\endcsname
\csname @\ifnum#1=0 first\else second\fi oftwo\endcsname
{%
\textbf{#2} \hfill {\textit{#3}}%
% \noindent right behind \\ has no effect as \\
% does _not_ cause the starting of a new paragraph. (Only the first
% line of a (new) paragraph would - without \noindent right _after_
% starting the paragraph - horizontally be indented by
%`\parindent`-glue.)
\\*%
\ignorespaces % This will make sure that a space(-token) right behind
% the closing brace of the environment's second
% non-optional argument will not yield horizontal glue.
}%
{%
\expandafter\let\csname end\csname @currenvir\endcsname\endcsname=\endcomment
\comment
}%
}%
{%
% Due to (La)TeX's \endlinechar-mechanism line-endings in the source usually
% are treated as if there were just spaces in the source. This in many
% situations leads to the coming into being of space-tokens. Space-tokens
% in turn in might yield undesired horizontal glue in the .pdf-output.
%
%\ifhmode\unskip\fi % In case the last thing produced by (La)TeX was glue,
% \unskip will remove that glue.
% The above might remove horizontal glue which comes into
% being at the end of the last line of the environment's body due to
% the \endlinechar-mechanism inserting a space at the end of lines.
%
% Instead you can end the last line of the environment-body with a
% comment-char = a percent-char = %.
\ignorespacesafterend % \ignorespacesafterend ensures that space-tokens
% behind \end{entry}, which might come into being due to the
% \endlinechar-mecnahnism, will not yield horizontal glue.
}
\newcounter{MyNiceDemoCounter}
\newcommand*\PrintMyNiceDemoCountersValue[1]{%
\noindent
\rlap{\lower-\ht\strutbox\hbox to\hsize{\hrulefill\null}}%
#1 the environment the value of
{\csname verbatim@font\endcsname MyNiceDemoCounter}
is \arabic{MyNiceDemoCounter}.\hfill\null
\llap{\lower\dp\strutbox\hbox to\hsize{\hrulefill\null}}%
}%
\begin{document}
\PrintMyNiceDemoCountersValue{Before}\bigskip
X\begin{entry}{bold}{italic}
This environment is printed and
\stepcounter{MyNiceDemoCounter}%
\verb|MyNiceDemoCounter| is stepped.
\end{entry}
X
\bigskip\PrintMyNiceDemoCountersValue{After}
\vfill
\PrintMyNiceDemoCountersValue{Before}\bigskip
X\begin{entry}[1]{bold}{italic}
This environment is not printed and
\stepcounter{MyNiceDemoCounter}%
\verb|MyNiceDemoCounter| is not stepped.
\end{entry}
X
\bigskip\PrintMyNiceDemoCountersValue{After}
\vfill
\PrintMyNiceDemoCountersValue{Before}\bigskip
X\begin{entry}{bold}{italic}
This environment is printed and
\stepcounter{MyNiceDemoCounter}%
\verb|MyNiceDemoCounter| is stepped.%
\end{entry}
X
\bigskip\PrintMyNiceDemoCountersValue{After}
\vfill
\PrintMyNiceDemoCountersValue{Before}\bigskip
X\begin{entry}[1]{bold}{italic}
This environment is not printed and
\stepcounter{MyNiceDemoCounter}%
\verb|MyNiceDemoCounter| is not stepped.%
\end{entry}
X
\bigskip\PrintMyNiceDemoCountersValue{After}
\end{document}