当参数有嵌套括号时,测试参数是否为“空”

当参数有嵌套括号时,测试参数是否为“空”

我试图在实践中测试一个参数是否为“空”,而不仅仅是“技术上”。请考虑以下 MWE 来了解我的意思:

\documentclass{article}
\usepackage{xifthen}
\begin{document}

\def\ansOne#1{%
    \ifx\fish#1\fish%
    \else%
    correct%
    \fi%
}

1) \ansOne{}% This returns nothing as intended

2) \ansOne{{}}% This returns "correct" when I want it to return nothing

3) \ansOne{{}{}{}}% This returns "correct" when I want it to return nothing

4) \ansOne{correct}% This returns "correct" as intended
\end{document}

我找到了有关各种测试以查看参数是否为空的精彩描述这里这是我得到上述技术的地方。然而,经过测试后,它们似乎都存在这个问题。我猜是因为 latex 只扩展了最外面的括号,然后由于里面有“标记”,所以它算作不为空并返回“正确”。

要清楚;我需要一个宏,如果内部只有括号或空格,则它绝对不返回任何内容(但我知道它是平衡的,所以没有不平衡的 if、括号等),如果有其他内容,则返回“正确”(或其他内容)。

有没有办法进行某种递归标记扩展来最终发现括号是已填充还是为空? 它会遇到问题吗,比如 \ansOne{{}{ }}? 或者空格会像往常一样被吃掉吗?

编辑:添加实用程序

一个适合使用这种特殊情况的地方是,如果你有一个自定义命令,它为 \item 提供一些格式选项,并采用可选参数来产生效果。例如;

\newcommand{\choice}[2][]{\item You typed in #2. 
    \ifthenelse{\equal{#1}{correct}}{Your answer is correct! Good work!}{}
    \ifthenelse{\equal{#1}{}}{Your answer is incorrect, try again.}{}
}

\begin{enumerate}
\choice[correct]{This should mark as correct}
\choice[]{This should mark as incorrect}
\choice[Nonsense]{This should just be repeated, not marked correct or incorrect}
\choice[\ansOne{{{}}}]{This should be marked incorrect, ideally}
\choice[\ansOne{{{correct}}{}{}}]{This should be marked as correct, ideally}.
\end{enumerate}

目前,此方法失败,因为 \ansOne 似乎无法以某种智能方式及时扩展以供 \choice 命令处理。我尝试编辑的 cls 文件中的某些格式选项使 \expandafter 在 \choice 命令之前失败。事实上,如果您在我的 cls 类型中编译上述代码,它会返回“缺少某些内容,可能是 \item”错误,通常在您没有以 \item 开头时会发现此错误。如果我可以让 \ansOne 在 \choice 之前立即扩展,我确信所有这些问题都会消失,但我知道唯一可以尝试的是 \expandafter,但它似乎不起作用。我在下面包含了完整的 multipleChoice 环境和 \choices 命令代码,但它利用了 tex4ht(我不太了解)将其挂接到 html 中,因此请自行承担阅读风险 :)

%% multiple choice environment
\renewcommand{\theenumi}{\textup{(\alph{enumi})}}
\renewcommand{\labelenumi}{\theenumi}
\renewcommand{\theenumii}{\textup{(\roman{enumii})}}
\renewcommand{\labelenumii}{\theenumii}

%% correct answers for multiple choice
\makeatletter
\define@key{choice}{value}[]{\def\choice@value{#1}}
\define@boolkey{choice}{correct}[true]{\def\choice@correct{#1}}
\setkeys{choice}{correct=false,value=}
\newcommand{\choice}[2][]{%
\setkeys{choice}{#1}%
\item #2\ifthenelse{\boolean{\choice@correct}}{\ifhandout \else \,\checkmark\,\setkeys{choice}{correct=false}\fi}{}}
\makeatother

%% Not really meant to be used outside this package (used in wordChoice)
%% \otherchoice outputs the item if correct and nothing if incorrect.
\makeatletter
\define@key{otherchoice}{value}[]{\def\otherchoice@value{#1}}
\define@boolkey{otherchoice}{correct}[true]{\def\otherchoice@correct{#1}}
\setkeys{otherchoice}{correct=false,value=}
\newcommand{\otherchoice}[2][]{%
  \ignorespaces
  \setkeys{otherchoice}{#1}
  \ifthenelse{\boolean{\otherchoice@correct}}{#2\setkeys{otherchoice}{correct=false}}{}\ignorespaces}
\makeatother
%%%

%setting default values for multipleChoice command key=value pairs
\makeatletter
\define@key{multipleChoice}{id}{\def\mc@id{#1}}
\setkeys{multipleChoice}{id=}

\newenvironment{multipleChoice}[1][]
{\setkeys{multipleChoice}{#1}%
\recordvariable{\mc@id}%
\begin{trivlist}\item[\hskip \labelsep\small\bfseries Multiple Choice:]\hfil\begin{enumerate}}
{\end{enumerate}\end{trivlist}}

%multipleChoice* is for internal use only! (used in wordChoice)
%It displays all choices in a list separated by /
\newenvironment{multipleChoice*}[1][]
{\setkeys{multipleChoice}{#1}%
\ifthenelse{\equal{\mc@id}{}}{}{\immediate\write\idfile{var \mc@id;}}%
\begin{enumerate*}[before={\upshape{(\hspace{-.25em}}}, itemjoin={/\hspace{-.25em}}, after={\upshape{)}}, label={}]}
{\end{enumerate*}}
\makeatother

完整的 cls 文件可以在这里找到这里

答案1

您可以用expl3它来迭代子组并检查它们是否为空。

\documentclass{article}
\usepackage{xparse}
\begin{document}

\ExplSyntaxOn

\NewDocumentCommand \ansOne { m }
{
  \bool_set_true:N \l_tmpa_bool
  \tl_map_inline:nn { #1 }
  {
    \tl_if_blank:nF { ##1 } { \bool_set_false:N \l_tmpa_bool }
  }
  \bool_if:NF \l_tmpa_bool { correct }
}

\ExplSyntaxOff

\ansOne{} % returns nothing
\ansOne{{}} % returns nothing
\ansOne{{}{}{}} % returns nothing

\ansOne{{}{ }} % returns nothing

\ansOne{{abc}{def}} % returns "correct"

\end{document}

您也可以在 Lua 中执行此操作。这样做的好处是空组的嵌套级别无关紧要(其他解决方案仅支持一层),并且宏是完全可扩展的,即 之后的\edef\x{\ansOne{{abc}{def}}}内容\x将是correct

\documentclass{article}
\begin{document}

\newcommand\ifonlybracesandspaces[1]{%
  \ifcase\directlua{%
    local input = "\detokenize{#1}"
    % Remove all {, }, and space from input
    local output = string.gsub(input, "[{} ]", "")
    if output == '' then
        tex.sprint(0)
    else
        tex.sprint(1)
    end
  }\relax
}

\newcommand\ansOne[1]{%
  \ifonlybracesandspaces{#1}%
    % Do nothing
  \else
    correct
  \fi
}

\ansOne{} % returns nothing
\ansOne{{}} % returns nothing
\ansOne{{}{}{}} % returns nothing

\ansOne{{}{ }} % returns nothing

\ansOne{{abc}{def}} % returns "correct"

\end{document}

答案2

当然可以,\setbox0=\hbox{\ignorespaces#1}\ifdim\wd0=0pt但是这不是可扩展测试。可扩展测试可以\isXempty{param}\iftrue在这里定义:

\def\isXempty#1#2{\expandafter\isXemptyA\detokenize{#1}\end}
\def\isXemptyA#1{%
   \ifx\end#1\csname iftrue\expandafter\endcsname
   \else \if\string{#1\expandafter\expandafter\expandafter \isXemptyA
         \else \if\string}#1\isXemptyB 
               \else \isXemptyC
   \fi   \fi   \fi
}
\def\isXemptyB#1\fi\fi\fi{\fi\fi\fi\isXemptyA}
\def\isXemptyC#1\end{\fi\fi\fi\iffalse}

% test:

\isXempty{}\iftrue YES\else NO\fi          % returns YES

\isXempty{{} {{ }}}\iftrue YES\else NO\fi  % returns YES

\isXempty{{x}}\iftrue YES\else NO\fi       % returns NO

\isXempty{{}{}{}}\iftrue YES\else NO\fi    % returns YES

\bye

相关内容