ocgx2、xparse、expl3:拆分逗号分隔的 OCMD/OCGs 参数列表以实现引用自动化

ocgx2、xparse、expl3:拆分逗号分隔的 OCMD/OCGs 参数列表以实现引用自动化

从应用层面来看,这个问题与之前关于使用 OCG 的测验(MCQ)的问题类似。但似乎(我怀疑)问题可能来自扩展概念和/或 LaTeX3 语法,即如何传递参数列表,因此涉及一个更一般的主题。不幸的是,目前我对这些主题一点都不熟悉。

一、概述

感谢 AlexG,该ocgx2软件包最近已更新,以支持 OCMD PDF 功能:可选内容成员词典:

(0)CTAN 更新:ocgx2

该软件包具有简单的界面,ocgx2可用于管理测验/选择题,请参阅 AlexG 的回答:

(1)ocgx2:使用 OCG 验证和重置多项选择题

然而,如果一个人要提供许多测验,管理所有不同的参考/ID 很快就会变得乏味。因此,目标是使参考设置自动化。通过将 MCQ 提案包装在问题环境中并通过计数器为问题中的每个提案分配一个唯一的 ID,可以轻松实现这一点。

II. − 自动设置标识符

\quizButton借用(1)中给出的命令的定义,我们得到:

已加载的包有(参见 MWE):ocgx2( so xparse) fontawesome5,,,xcolorcalc

\newcounter{quizquestion}% Question counter within a quiz
\newcounter{mcqproposal}[quizquestion]% Proposal counter to choose within a question

%- Defining a multiple solution MCQ environment
\NewDocumentEnvironment{quizquestion}{}{%
    \stepcounter{quizquestion}%
    \setcounter{mcqproposal}{0}%
    \vspace*{20pt}%
    \textbf{Question~\thequizquestion}\par\vspace*{8pt}%
}{}

%- Defining a proposition for multiple solution MCQ
\NewDocumentCommand{\mcqproposal}{m}{%
    % Syntax − #1: choice id for answering proposal
    \stepcounter{mcqproposal}%
    \fcolorbox{gray}{white}{%
        \parbox[t]{16pt}{%
            \hspace*{2pt}%
            \quizButton%
                {\faIcon[regular]{square}}%
                {verify:\thequizquestion}%
                {mcqproposalref:\thequizquestion:\themcqproposal}
        }%
        \parbox[t]{\linewidth-16pt}{mcqproposalref:\thequizquestion:\themcqproposal\quad #1}%
    }%
}

III. − 拆分并传递参数以自动化 OCMD 规则

下一步是通过\verifyButton(1) 中给出的命令来管理(验证)所有正确和不正确的可能答案。但目标还在于简化问题中的界面,只为正确和不正确的命题在逗号分隔的列表中排版 ID 号,例如\mcqverify{1,3,4}{2,5}

为此,我们必须拆分标识符列表,添加提案的自动命名,并最终将新标识符重新引入为 OCMD 参数。问题出现在最后阶段,并导致不良结果或编译错误。

有很多方法可以拆分逗号分隔的参数列表。下面给出了几个示例。

要检索由具有足够的第一项和最后一项的分隔符分隔的参数列表,可以参考以下链接:

(2)软件包 xparse \SplitList 最后一个标记

(3)pgffor:对 \foreach-list 中最后一项进行特殊处理

a) 使用循环:纯 LaTeX 方式,无需包

(4)我可以从列表中自动抽取我的卡片吗?→ Bordaigorl 的回答

\makeatletter
    \newcommand{\mcqSplitLoop}[1]{%
        \@for\@c:=#1\do{%
            \ifnum\numexpr\@c=1%
                mcqproposalref:\thequizquestion:\@c%
            \else%
                ,mcqproposalref:\thequizquestion:\@c%
            \fi%
        }%
    }
\makeatother

b) 使用循环:pgffor

(5)pgffor:对 \foreach-list 中最后一项进行特殊处理→ Camandir 的回答

\NewDocumentCommand{\mcqSplitDef}{m}{%
    \foreach \n [count=\ni] in {#1} {%
        \ifnum\ni=1%
            mcqproposalref:\thequizquestion:\n%
        \else%
            ,mcqproposalref:\thequizquestion:\n%
        \fi%
    }%
}

c) 使用\SplitList来自xparse

(6)软件包 xparse \SplitList 最后一个标记 → egreg 的答案第二种解决方案

\NewDocumentCommand{\mcqSplitArguments}{>{\SplitList{,}} m}{%
    {\ProcessList{#1}{\mcqSplitItems}}%
}
\newcommand{\mcqSplitItems}[1]{%
    mcqproposalref:\thequizquestion:#1\let\mcqSplitItems\mcqSplitItemsAdd%
}
\newcommand{\mcqSplitItemsAdd}[1]{,mcqproposalref:\thequizquestion:#1}

d) 使用 LaTeX3 语法

(7)软件包 xparse \SplitList 最后一个标记→ egreg 的答案第三种解决方案

\newcommand{\ejazzArgsIds}[1]{%
    mcqproposalref:\thequizquestion:#1%
}
\ExplSyntaxOn
\NewDocumentCommand\mcqejazzList{m}{
  \ejazz_process_list:n {#1}
}
% define a sequence for storing the "massaged" items
\seq_new:N \l_ejazz_items_seq
\cs_new_protected:Npn \ejazz_process_list:n #1{
    % clear the sequence
    \seq_clear:N \l_ejazz_items_seq
    % cycle through the arguments, storing "\ejazzArgsIds{<arg>}" in the sequence
    \clist_map_inline:nn { #1 }{
        \seq_put_right:Nn \l_ejazz_items_seq { \ejazzArgsIds{##1} }
    }
    % output the sequence putting "," in between elements
    \seq_use:Nnnn \l_ejazz_items_seq { , } { , } { , }
}
\ExplSyntaxOff

IV. 结果

还测试了其他方法,甚至一些愚蠢的方法。所有这些方法都正确显示了参数列表,但在尝试以下语法时也会编译失败:\mcqverify{\newarglist{<correct-number-ids>}}{\newarglist{<wrong-number-ids>}},其中\mcqverify改编自\verifyButton(1) 中给出的语法。

如果在命令定义中修改了参数列表,它们也无法编译\mcqverify

\NewDocumentCommand{\mcqverify}{m m}{%
  % Syntax − #1: list of OCG ids of correct/required answer(s), comma separated
  %          #2: list of OCG ids of wrong answer(s), comma separated
  \showocg{verify:\thequizquestion}{\fbox{\strut Verify}}
  \begin{ocg}{verify:\thequizquestion}{verify:\thequizquestion}{off}% verification layer
    \makebox[0pt][l]{%
      \begin{ocmd}{\Not{\And{#1,\Not{\Or{#2}}}}}% "wrong" layer (OCMD)
        \hspace*{0.25em}\textcolor{red!60!black}{\faTimes}%Wrong.
      \end{ocmd}%
    }%
    \begin{ocmd}{\And{#1,\Not{\Or{#2}}}}% "correct" layer (OCMD)
      \hspace*{0.25em}\textcolor{green!60!black}{\faCheck}%Correct.
    \end{ocmd}%
  \end{ocg}%
}

五、结论

虽然我不熟悉 LaTeX3 语法,但对于\resetButton(1) 中的命令定义以及借用下面链接 (8) 的想法,我尝试了一些“经验”改编,但没有成功。

(8)将宏扩展为 xparse \SplitList 宏→ egreg 的回答

简而言之,我认为问题在于如何在 LaTeX3 中存储参数列表。这很遗憾,但它超出了我目前的技能范围……

那么,这个假设正确吗?我们如何才能得到可行的解决方案?

平均能量损失

\documentclass{article}
\usepackage{ocgx2}% loads `xparse' package
\usepackage{fontawesome5}
\usepackage{xcolor}
\usepackage{calc} %\widthof{...}
\usepackage{pgffor}

%-- New OCMDs implementation: `ocgx2` package
%--------------------------------------------

%- Quiz button
\NewDocumentCommand{\quizButton}{o m m m}{%
  % Syntax − #1: optional: radio button group
  %          #2: button shape: \faCircleThin, \faSquareO
  %          #3: verification id
  %          #4: choice id
  % action on click: toggle myself (choice layer), hide verifcation layer
  \makebox[0pt][l]{\actionsocg{#4}{}{#3}{#2}}%
  \begin{ocg}[\IfValueT{#1}{radiobtngrp=#1}]{#4}{#4}{off}% choice layer
    \makebox[\widthof{#2}]{\textcolor{green!60!black}{\faCheck}}%
  \end{ocg}%
}

%- Verify button
\NewDocumentCommand{\verifyButton}{m m m}{%
  % Syntax − #1: verification id
  %          #2: list of OCG ids of correct/required answer(s), comma separated
  %          #3: list of OCG ids of wrong answer(s), comma separated
  \showocg{#1}{\fbox{\strut Verify}}
  \begin{ocg}{#1}{#1}{off}% verification layer
    \makebox[0pt][l]{%
      \begin{ocmd}{\Not{\And{#2,\Not{\Or{#3}}}}}% "wrong" layer (OCMD)
        Wrong.
      \end{ocmd}%
    }%
    \begin{ocmd}{\And{#2,\Not{\Or{#3}}}}% "correct" layer (OCMD)
      Correct.
    \end{ocmd}%
  \end{ocg}%
}

%- Reset button
\ExplSyntaxOn
\NewDocumentCommand{\resetButton}{m}{%
  % Syntax − #1: list of OCG ids, comma separated
  \clist_set:Nn\l_tmpa_clist{#1} % save OCG ids as L3 clist variable
  \hideocg{\clist_use:Nn\l_tmpa_clist{~}}{\fbox{\strut Reset}}%
}
\ExplSyntaxOff

%-- Automated setting of references/IDs
%--------------------------------------

\newcounter{quizquestion}% Question counter within a quiz
\newcounter{mcqproposal}[quizquestion]% Proposal counter to choose within a question

%- Defining a multiple solutions MCQ environment
\NewDocumentEnvironment{quizquestion}{}{%
  % Syntax − #1: optional title
    \stepcounter{quizquestion}%
    \setcounter{mcqproposal}{0}%
    \vspace*{20pt}%
    \textbf{Question~\thequizquestion}\par\vspace*{8pt}%
}{}

%- Defining a proposition for multiple solutions MCQ
\NewDocumentCommand{\mcqproposal}{m}{%
    % Syntax − #1: choice id for answering proposal
    \stepcounter{mcqproposal}%
    \fcolorbox{gray}{white}{%
        \parbox[t]{16pt}{%
            \hspace*{2pt}%
            \quizButton%
                {\faIcon[regular]{square}}%
                {verify:\thequizquestion}%
                {mcqproposalref:\thequizquestion:\themcqproposal}
        }%
        \parbox[t]{\linewidth-16pt}{mcqproposalref:\thequizquestion:\themcqproposal\quad #1}%
    }%
}

% https://tex.stackexchange.com/questions/146979/can-i-automatically-draw-my-cards-from-a-list
\makeatletter
    \newcommand{\mcqSplitLoop}[1]{%
        \@for\@c:=#1\do{%
            \ifnum\numexpr\@c=1%
                mcqproposalref:\thequizquestion:\@c%
            \else%
                ,mcqproposalref:\thequizquestion:\@c%
            \fi%
        }%
    }
\makeatother

% https://tex.stackexchange.com/questions/16198/pgffor-special-treatment-for-last-item-in-foreach-list
\NewDocumentCommand{\mcqSplitDef}{m}{%
    \foreach \n [count=\ni] in {#1} {%
        \ifnum\ni=1%
            mcqproposalref:\thequizquestion:\n%
        \else%
            ,mcqproposalref:\thequizquestion:\n%
        \fi%
    }%
}

% https://tex.stackexchange.com/questions/110898/package-xparse-splitlist-last-token
%
% xparse
\NewDocumentCommand{\mcqSplitArguments}{>{\SplitList{,}} m}{%
    {\ProcessList{#1}{\mcqSplitItems}}%
}
\newcommand{\mcqSplitItems}[1]{%
    mcqproposalref:\thequizquestion:#1\let\mcqSplitItems\mcqSplitItemsAdd%
}
\newcommand{\mcqSplitItemsAdd}[1]{,mcqproposalref:\thequizquestion:#1}
%
% LaTeX3
\newcommand{\ejazzArgsIds}[1]{%
  mcqproposalref:\thequizquestion:#1%
}
\ExplSyntaxOn
\NewDocumentCommand\mcqejazzList{m}{
  \ejazz_process_list:n {#1}
}
% define a sequence for storing the "massaged" items
\seq_new:N \l_ejazz_items_seq
\cs_new_protected:Npn \ejazz_process_list:n #1{
  % clear the sequence
  \seq_clear:N \l_ejazz_items_seq
  % cycle through the arguments, storing "\ejazzArgsIds{<arg>}" in the sequence
  \clist_map_inline:nn { #1 }{
    \seq_put_right:Nn \l_ejazz_items_seq { \ejazzArgsIds{##1} }
  }
  % output the sequence putting "," in between elements
  \seq_use:Nnnn \l_ejazz_items_seq { , } { , } { , }
}
\ExplSyntaxOff

% https://tex.stackexchange.com/questions/19746/cunning-latex-tricks
\newcommand{\mcqsplitlist}[2][,]{%
    \def\itemdelim{\def\itemdelim{#1}}% Item delimiter delayed by one cycle
    \renewcommand*{\do}[1]{{\itemdelim mcqproposalref:\thequizquestion:##1\itemdelim}}% How to process each item
    \docsvlist{#2}% Process list
}

% https://tex.stackexchange.com/questions/166006/expand-macro-into-xparse-splitlist-macro
\newcommand{\argsIds}[1]{%
    \ifnum\numexpr#1=1
        mcqproposalref:\thequizquestion:#1%
    \else
        ,mcqproposalref:\thequizquestion:#1%
    \fi
}
\ExplSyntaxOn
\NewDocumentCommand\mcqSplitExpl{O{,} m O{\argsIds}}
 {
  \seq_set_split:Nnn \l_ejazzsplit_input_seq { #1 } { #2 }
  \seq_map_function:NN \l_ejazzsplit_input_seq #3
 }
\seq_new:N \l_ejazzsplit_input_seq
\ExplSyntaxOff

% Verify button splitting the comma separated lists of arguments
\ExplSyntaxOn
\NewDocumentCommand{\mcqverify}{m m}{%
  % Syntax − #1: list of OCG ids of correct/required answer(s), comma separated
  %          #2: list of OCG ids of wrong answer(s), comma separated
  %\clist_set:Nn\l_correct_clist{\mcqSplitDef{#1}} % save OCMD ids as L3 clist variable
  %\clist_set:Nn\l_wrong_clist{\mcqSplitDef{#2}} % save OCMD ids as L3 clist variable
  \showocg{verify:\thequizquestion}{\fbox{\strut Verify}}
  \begin{ocg}{verify:\thequizquestion}{verify:\thequizquestion}{off}% verification layer
    \makebox[0pt][l]{%
      %\begin{ocmd}{\Not{\And{\clist_use:Nn\l_correct_clist{~},\Not{\Or{\clist_use:Nn\l_wrong_clist{~}}}}}}% "wrong" layer (OCMD)
      \begin{ocmd}{\Not{\And{#1,\Not{\Or{#2}}}}}% "wrong" layer (OCMD)
        \hspace*{0.25em}\textcolor{red!60!black}{\faTimes}%Wrong.
      \end{ocmd}%
    }%
    %\begin{ocmd}{\And{\clist_use:Nn\l_correct_clist{~},\Not{\Or{\clist_use:Nn\l_wrong_clist{~}}}}}% "correct" layer (OCMD)
    \begin{ocmd}{\And{#1,\Not{\Or{#2}}}}% "correct" layer (OCMD)
      \hspace*{0.25em}\textcolor{green!60!black}{\faCheck}%Correct.
    \end{ocmd}%
  \end{ocg}%
}
\ExplSyntaxOff


\parindent=0pt
\begin{document}

%-- Manual setting of IDs

Which are the colour components of an RGB image?\\
Multiple required assertions.\\[8pt]
\quizButton{\faIcon[regular]{square}}{vrfyRGBCol}{magenta} Magenta.\\
\quizButton{\faIcon[regular]{square}}{vrfyRGBCol}{green2}  Green.\\
\quizButton{\faIcon[regular]{square}}{vrfyRGBCol}{cyan}    Cyan.\\
\quizButton{\faIcon[regular]{square}}{vrfyRGBCol}{blue}    Blue.\\
\quizButton{\faIcon[regular]{square}}{vrfyRGBCol}{red2}    Red.\\
\quizButton{\faIcon[regular]{square}}{vrfyRGBCol}{black2}  Black.\\
\quizButton{\faIcon[regular]{square}}{vrfyRGBCol}{yellow2} Yellow.\\[8pt]
\verifyButton{vrfyRGBCol}{red2,green2,blue}{cyan,magenta,yellow2,black2}\hspace{0.3\linewidth}
\resetButton{vrfyRGBCol,red2,green2,blue,cyan,magenta,yellow2,black2}

%-- Automatic setting of IDs

\begin{quizquestion}
Which are the colour components of an RGB image?\\
Multiple required assertions.\\[8pt]
\mcqproposal{Green.}\\[2pt]
\mcqproposal{Cyan.}\\[2pt]
\mcqproposal{Blue.}\\[2pt]
\mcqproposal{Red.}\\[2pt]
\mcqproposal{Yellow.}\\[8pt]

%- Full typesetting the lists of ids ← Works (obvious: `ocgx2` package interface)
%\mcqverify{mcqproposalref:\thequizquestion:1,mcqproposalref:\thequizquestion:3,mcqproposalref:\thequizquestion:4}{mcqproposalref:\thequizquestion:2,mcqproposalref:\thequizquestion:5}%
%\verifyButton{verify:\thequizquestion}{mcqproposalref:\thequizquestion:1,mcqproposalref:\thequizquestion:3,mcqproposalref:\thequizquestion:4}{mcqproposalref:\thequizquestion:2,mcqproposalref:\thequizquestion:5}

%- Manual splitting of the lists of IDs (rather stupid) ← Compiles but does not work
\def\ocmdlistcorrect{mcqproposalref:\thequizquestion:1,mcqproposalref:\thequizquestion:3,mcqproposalref:\thequizquestion:4}%
\def\ocmdlistwrong{mcqproposalref:\thequizquestion:2,mcqproposalref:\thequizquestion:5}%
% Does not work: checking all correct answers with any wrong answer give a correct result
%\mcqverify{mcqproposalref:\thequizquestion:1,mcqproposalref:\thequizquestion:3,mcqproposalref:\thequizquestion:4}{\ocmdlistwrong}
% Does not work: checking empty, one or more correct answer(s) give a correct result
%\mcqverify{\ocmdlistcorrect}{mcqproposalref:\thequizquestion:2,mcqproposalref:\thequizquestion:5}
% Does not work: checking any/all answers give a correct result
%\mcqverify{\ocmdlistcorrect}{\ocmdlistwrong}

%- Good list of arguments, but fails to compile
%{\scriptsize Correct: \mcqSplitDef{1,3,4}}\par
%{\scriptsize Wrong: \mcqSplitDef{2,5}}\par
%\mcqverify{\mcqSplitDef{1,3,4}}{\mcqSplitDef{2,5}}
%{\scriptsize Correct: \mcqSplitExpl{1,3,4}}\par
%{\scriptsize Correct: \mcqSplitLoop{1,3,4}}\par
%{\scriptsize Wrong : \mcqSplitExpl{\ocmdlistwrong}}\par%
%\mcqverify{\mcqSplitExpl{1,3,4}}{\mcqSplitExpl{2,5}}

\mcqejazzList{1,3,4}

%\mcqverify{1,3,4}{2,5} ← Interface to reach

%\hspace{0.3\linewidth}\mcqreset{1,2,3,4,5}
%or \mcqreset (with a loop and a counter or anything else)
\end{quizquestion}

\end{document}

答案1

让我们看一下expl3变体:

\newcommand{\ejazzArgsIds}[1]{%
    mcqproposalref:\thequizquestion:#1%
}
\ExplSyntaxOn
\NewDocumentCommand\mcqejazzList{m}{
  \ejazz_process_list:n {#1}
}
% define a sequence for storing the "massaged" items
\seq_new:N \l_ejazz_items_seq
\cs_new_protected:Npn \ejazz_process_list:n #1{
    % clear the sequence
    \seq_clear:N \l_ejazz_items_seq
    % cycle through the arguments, storing "\ejazzArgsIds{<arg>}" in the sequence
    \clist_map_inline:nn { #1 }{
        \seq_put_right:Nn \l_ejazz_items_seq { \ejazzArgsIds{##1} }
    }
    % output the sequence putting "," in between elements
    \seq_use:Nnnn \l_ejazz_items_seq { , } { , } { , }
}
\ExplSyntaxOff

您可能已经意识到,这输出了正确的内容。那么为什么它在 中不起作用\mcqverify?您可以尝试例如

\clist_set:Nn\l_correct_clist{\mcqSplitDef{#1}}

这里 clist 实际上是设置为带有元素的单个元素列表\mcqSplitDef{#1},因为参数中没有逗号。尤其是 expl3才不是\mcqSplitDef自行展开。如果要\mcqSplitDef展开,则需要使用\clist_set:Nf而不是\clist_set:Nn:Nx也可以,但:Nf这里 就够了)。如果你尝试这样做,它仍然不起作用。为什么?\mcqSplitDef不是“可扩展的”。 (这可以通过\cs_new_protected而不是看到\cs_new。仅仅删除_protected也无济于事,因为那里使用的宏不可扩展。)因此有两个选择:编写完全可扩展的版本\ejazz_process_list:n(很难)或找到另一种方法来传递结果列表而不将其用作扩展结果。例如,您可以\clist_set:Nf直接在 内部执行\ejazz_process_list,那里是可扩展的。如果我们转换成\seq_use:Nnnn就更容易了,可以直接在 中使用。当然,现在必须支持不同的序列,所以我们添加一个额外的参数:\l_correct_clist\l_correct_seqseq\ejazz_process_list:n\ejazz_process_list:n

\cs_new_protected:Npn \ejazz_process_list:Nn #1 #2{
  % clear the sequence
  \seq_clear:N #1
  % cycle through the arguments, storing "\ejazzArgsIds{<arg>}" in the sequence
  \clist_map_inline:nn { #2 }{
    \seq_put_right:Nx #1 { \ejazzArgsIds{##1} }% Here :Nx instead of :Nn ensures that \ejazzArgsId is actually evaluated instead of passed as-is
  }
}

我们如何在里面使用它\mcqverify?首先我们声明我们的序列

\seq_new:N \l_correct_seq
\seq_new:N \l_wrong_seq

然后我们可以mcqverify使用我们的\ejazz_process_list:Nn

\NewDocumentCommand{\mcqverify}{m m}{%
  % Syntax − #1: list of OCG ids of correct/required answer(s), comma separated
  %          #2: list of OCG ids of wrong answer(s), comma separated
  \ejazz_process_list:Nn \l_correct_seq { #1 } % save OCMD ids as L3 seq variable
  \ejazz_process_list:Nn \l_wrong_seq { #2 } % save OCMD ids as L3 seq variable

\l_correct_seq现在我们可以使用和\l_wrong_seq访问\seq_use:Nn \l_correct_seq {,}和的逗号分隔值\seq_use:Nn \l_wrong_seq {,}。这是可扩展的,但 ocmd 的参数不是 expl3 参数,因此我们不能通过将 a 替换:n为 a 来强制扩展。这让事情变得更加复杂。所以让我们来一个解决方法:我们只使用一个将已经扩展的列表作为参数的:f本地宏。(这使得定义更容易,因为我们不必担心扩展)现在我们使用\ejazz__verify:nn\ejazz__verify:nn

  \ejazz__verify:ff { \seq_use:Nn \l_correct_seq {,} } { \seq_use:Nn \l_wrong_seq {,} }
}

告诉expl3扩展这两个参数f。现在\ejazz__verify:nn直截了当:

\cs_new_protected:Npn \ejazz__verify:nn #1 #2 {
  \showocg{verify:\thequizquestion}{\fbox{\strut Verify}}
  \begin{ocg}{verify:\thequizquestion}{verify:\thequizquestion}{off}% verification layer
    \makebox[0pt][l]{%
      \begin{ocmd}{\Not{\And{#1,\Not{\Or{#2}}}}}% "wrong" layer (OCMD)
        \hspace*{0.25em}\textcolor{red!60!black}{\faTimes}%Wrong.
      \end{ocmd}%
    }%
    \begin{ocmd}{\And{#1,\Not{\Or{#2}}}}% "correct" layer (OCMD)
      \hspace*{0.25em}\textcolor{green!60!black}{\faCheck}%Correct.
    \end{ocmd}%
  \end{ocg}%
}

\ejazz__verify:ff你还记得如果我们想扩展的参数,只需使用的部分\ejazz__verify:nn吗?这并不那么容易,我们必须告诉expl3我们我们想要使用\ejazz__verify:ff,这样expl3才能准备这个宏。所以我们添加

\cs_generate_variant:Nn \ejazz__verify:nn {ff}

好的,让我们把它们放在一起:

\documentclass{article}
\usepackage{ocgx2}% loads `xparse' package
\usepackage{fontawesome5}
\usepackage{xcolor}
\usepackage{calc} %\widthof{...}
\usepackage{pgffor}

%-- New OCMDs implementation: `ocgx2` package
%--------------------------------------------

%- Quiz button
\NewDocumentCommand{\quizButton}{o m m m}{%
  % Syntax − #1: optional: radio button group
  %          #2: button shape: \faCircleThin, \faSquareO
  %          #3: verification id
  %          #4: choice id
  % action on click: toggle myself (choice layer), hide verifcation layer
  \makebox[0pt][l]{\actionsocg{#4}{}{#3}{#2}}%
  \begin{ocg}[\IfValueT{#1}{radiobtngrp=#1}]{#4}{#4}{off}% choice layer
    \makebox[\widthof{#2}]{\textcolor{green!60!black}{\faCheck}}%
  \end{ocg}%
}

%- Verify button
\NewDocumentCommand{\verifyButton}{m m m}{%
  % Syntax − #1: verification id
  %          #2: list of OCG ids of correct/required answer(s), comma separated
  %          #3: list of OCG ids of wrong answer(s), comma separated
  \showocg{#1}{\fbox{\strut Verify}}
  \begin{ocg}{#1}{#1}{off}% verification layer
    \makebox[0pt][l]{%
      \begin{ocmd}{\Not{\And{#2,\Not{\Or{#3}}}}}% "wrong" layer (OCMD)
        Wrong.
      \end{ocmd}%
    }%
    \begin{ocmd}{\And{#2,\Not{\Or{#3}}}}% "correct" layer (OCMD)
      Correct.
    \end{ocmd}%
  \end{ocg}%
}

%- Reset button
\ExplSyntaxOn
\NewDocumentCommand{\resetButton}{m}{%
  % Syntax − #1: list of OCG ids, comma separated
  \clist_set:Nn\l_tmpa_clist{#1} % save OCG ids as L3 clist variable
  \hideocg{\clist_use:Nn\l_tmpa_clist{~}}{\fbox{\strut Reset}}%
}
\ExplSyntaxOff

%-- Automated setting of references/IDs
%--------------------------------------

\newcounter{quizquestion}% Question counter within a quiz
\newcounter{mcqproposal}[quizquestion]% Proposal counter to choose within a question

%- Defining a multiple solutions MCQ environment
\NewDocumentEnvironment{quizquestion}{}{%
  % Syntax − #1: optional title
    \stepcounter{quizquestion}%
    \setcounter{mcqproposal}{0}%
    \vspace*{20pt}%
    \textbf{Question~\thequizquestion}\par\vspace*{8pt}%
}{}

%- Defining a proposition for multiple solutions MCQ
\NewDocumentCommand{\mcqproposal}{m}{%
    % Syntax − #1: choice id for answering proposal
    \stepcounter{mcqproposal}%
    \fcolorbox{gray}{white}{%
        \parbox[t]{16pt}{%
            \hspace*{2pt}%
            \quizButton%
                {\faIcon[regular]{square}}%
                {verify:\thequizquestion}%
                {mcqproposalref:\thequizquestion:\themcqproposal}
        }%
        \parbox[t]{\linewidth-16pt}{mcqproposalref:\thequizquestion:\themcqproposal\quad #1}%
    }%
}

%
% LaTeX3
\newcommand{\ejazzArgsIds}[1]{%
  mcqproposalref:\thequizquestion:#1%
}
\ExplSyntaxOn
% define a sequence for storing the "massaged" items
\cs_new_protected:Npn \ejazz_process_list:Nn #1 #2{
  % clear the sequence
  \seq_clear:N #1
  % cycle through the arguments, storing "\ejazzArgsIds{<arg>}" in the sequence
  \clist_map_inline:nn { #2 }{
    \seq_put_right:Nx #1 { \ejazzArgsIds{##1} }
  }
}
\ExplSyntaxOff

% Verify button splitting the comma separated lists of arguments
\ExplSyntaxOn
\seq_new:N \l_correct_seq
\seq_new:N \l_wrong_seq
\cs_new_protected:Npn \ejazz__verify:nn #1 #2 {
  \showocg{verify:\thequizquestion}{\fbox{\strut Verify}}
  \begin{ocg}{verify:\thequizquestion}{verify:\thequizquestion}{off}% verification layer
    \makebox[0pt][l]{%
      \begin{ocmd}{\Not{\And{#1,\Not{\Or{#2}}}}}% "wrong" layer (OCMD)
        \hspace*{0.25em}\textcolor{red!60!black}{\faTimes}%Wrong.
      \end{ocmd}%
    }%
    \begin{ocmd}{\And{#1,\Not{\Or{#2}}}}% "correct" layer (OCMD)
      \hspace*{0.25em}\textcolor{green!60!black}{\faCheck}%Correct.
    \end{ocmd}%
  \end{ocg}%
}
\cs_generate_variant:Nn \ejazz__verify:nn {ff}
\NewDocumentCommand{\mcqverify}{m m}{%
  % Syntax − #1: list of OCG ids of correct/required answer(s), comma separated
  %          #2: list of OCG ids of wrong answer(s), comma separated
  \ejazz_process_list:Nn \l_correct_seq { #1 } % save OCMD ids as L3 seq variable
  \ejazz_process_list:Nn \l_wrong_seq { #2 } % save OCMD ids as L3 seq variable
  \ejazz__verify:ff { \seq_use:Nn \l_correct_seq {,} } { \seq_use:Nn \l_wrong_seq {,} }
}
\ExplSyntaxOff


\parindent=0pt
\begin{document}

%-- Manual setting of IDs

Which are the colour components of an RGB image?\\
Multiple required assertions.\\[8pt]
\quizButton{\faIcon[regular]{square}}{vrfyRGBCol}{magenta} Magenta.\\
\quizButton{\faIcon[regular]{square}}{vrfyRGBCol}{green2}  Green.\\
\quizButton{\faIcon[regular]{square}}{vrfyRGBCol}{cyan}    Cyan.\\
\quizButton{\faIcon[regular]{square}}{vrfyRGBCol}{blue}    Blue.\\
\quizButton{\faIcon[regular]{square}}{vrfyRGBCol}{red2}    Red.\\
\quizButton{\faIcon[regular]{square}}{vrfyRGBCol}{black2}  Black.\\
\quizButton{\faIcon[regular]{square}}{vrfyRGBCol}{yellow2} Yellow.\\[8pt]
\verifyButton{vrfyRGBCol}{red2,green2,blue}{cyan,magenta,yellow2,black2}\hspace{0.3\linewidth}
\resetButton{vrfyRGBCol,red2,green2,blue,cyan,magenta,yellow2,black2}

%-- Automatic setting of IDs

\begin{quizquestion}
Which are the colour components of an RGB image?\\
Multiple required assertions.\\[8pt]
\mcqproposal{Green.}\\[2pt]
\mcqproposal{Cyan.}\\[2pt]
\mcqproposal{Blue.}\\[2pt]
\mcqproposal{Red.}\\[2pt]
\mcqproposal{Yellow.}\\[8pt]

\mcqverify{1,3,4}{2,5} %<== Interface to reach

%\hspace{0.3\linewidth}\mcqreset{1,2,3,4,5}
%or \mcqreset (with a loop and a counter or anything else)
\end{quizquestion}

\end{document}

这必须至少编译两次才能修复所有 OCG。

屏幕上显示的结果包括典型的错误答案和正确答案

在此处输入图片描述

答案2

[0.54 2022/04/01]pkg版本开始ocgx2ocmd环境扩展了其参数,允许使用扩展为逗号分隔的 OCG ID 列表的宏和命令。这样就无需将环境包装ocmd在带有扩展参数的外部命令中:

\def\correctlist{A,B,C,E}
\def\wronglist{D,F}
\begin{ocmd}{\And{\correctlist,\Not{\Or{\wronglist}}}
\end{ocmd}
\begin{ocmd}{\Not{\And{\correctlist,\Not{\Or{\wronglist}}}}
\end{ocmd}

问题示例:

\documentclass{article}

\usepackage{ocgx2}[2022/04/01] % loads `xparse' package
\usepackage{fontawesome5}
\usepackage{xcolor}
\usepackage{calc} %\widthof{...}

%-- New OCMDs implementation: `ocgx2' package
%--------------------------------------------

%- Quiz button
\NewDocumentCommand{\quizButton}{o m m m}{%
  % Syntax − #1: optional: radio button group
  %          #2: button shape: \faCircleThin, \faSquareO
  %          #3: verification id
  %          #4: choice id
  % action on click: toggle myself (choice layer), hide verifcation layer
  \makebox[0pt][l]{\actionsocg{#4}{}{#3}{#2}}%
  \begin{ocg}[\IfValueT{#1}{radiobtngrp=#1}]{#4}{#4}{off}% choice layer
    \makebox[\widthof{#2}]{\textcolor{green!60!black}{\faCheck}}%
  \end{ocg}%
}

%-- Automated setting of references/IDs
%--------------------------------------

\newcounter{quizquestion}% Question counter within a quiz
\newcounter{mcqproposal}[quizquestion]% Proposal counter to choose within a question

%- Defining a multiple solutions MCQ environment
\NewDocumentEnvironment{quizquestion}{}{%
  % Syntax − #1: optional title
  \stepcounter{quizquestion}%
  \setcounter{mcqproposal}{0}%
  \vspace*{20pt}%
  \textbf{Question~\thequizquestion}\par\vspace*{8pt}%
}{}

%- Defining a proposition for multiple solutions MCQ
\NewDocumentCommand{\mcqproposal}{m}{%
  % Syntax − #1: choice id for answering proposal
  \stepcounter{mcqproposal}%
  \fcolorbox{gray}{white}{%
    \parbox[t]{16pt}{%
      \hspace*{2pt}%
      \quizButton%
        {\faIcon[regular]{square}}%
        {verify:\thequizquestion}%
        {mcqproposalref:\thequizquestion:\themcqproposal}
    }%
    \parbox[t]{\linewidth-16pt}{mcqproposalref:\thequizquestion:\themcqproposal\quad #1}%
  }%
}

\ExplSyntaxOn % LaTeX3

% defines and fills an L3 sequence with OCG ids obtained from comma-separated list of answer ids
\cs_new_protected_nopar:Nn\ejazz_process_list:Nnn{% args: L3 seq variable, question number, comma list of answer ids
  % create/clear the sequence
  \seq_clear_new:N#1
  % cycle through comma list (3rd argument)
  \clist_map_inline:nn{#3}{\seq_put_right:Nn#1{mcqproposalref:#2:##1}}
}

% Verify button splitting the comma separated lists of arguments
\NewDocumentCommand{\mcqverify}{m m}{%
  % Syntax − #1: list of correct/required answer id(s), comma separated
  %          #2: list of wrong answer id(s), comma separated
  \ejazz_process_list:Nnn\l_correct_seq{\thequizquestion}{#1} % save "correct" OCG ids as L3 seq variable
  \ejazz_process_list:Nnn\l_wrong_seq{\thequizquestion}{#2} % save "wrong" OCG ids as L3 seq variable
  \showocg{verify:\thequizquestion}{\fbox{\strut Verify}}
  \begin{ocg}{verify:\thequizquestion}{verify:\thequizquestion}{off}% verification layer
    \makebox[0pt][l]{%
      % ocmd evironment now expanding its argument :-)
      \begin{ocmd}{\Not{\And{\seq_use:Nn\l_correct_seq{,},\Not{\Or{\seq_use:Nn\l_wrong_seq{,}}}}}}% "wrong" layer (OCMD)
        \hspace*{0.25em}\textcolor{red!60!black}{\faTimes}%Wrong.
      \end{ocmd}%
    }%
    \begin{ocmd}{\And{\seq_use:Nn\l_correct_seq{,},\Not{\Or{\seq_use:Nn\l_wrong_seq{,}}}}}% "correct" layer (OCMD)
      \hspace*{0.25em}\textcolor{green!60!black}{\faCheck}%Correct.
    \end{ocmd}%
  \end{ocg}%
}

\ExplSyntaxOff

\parindent=0pt

\begin{document}

%-- Automatic setting of IDs

\begin{quizquestion}
Which are the colour components of an RGB image?\\
Multiple required assertions.\\[8pt]
\mcqproposal{Green.}\\[2pt]
\mcqproposal{Cyan.}\\[2pt]
\mcqproposal{Blue.}\\[2pt]
\mcqproposal{Red.}\\[2pt]
\mcqproposal{Yellow.}\\[8pt]

\mcqverify{1,3,4}{2,5}

\end{quizquestion}

\end{document}

相关内容