使用 xsim 练习中的任务作为参考的自定义标签

使用 xsim 练习中的任务作为参考的自定义标签

为了创建作业表,我使用了xsim包,为了在问题中创建子问题,我使用了包tasks。问题出现在引用子问题时。理想情况下,我希望子问题被称为 1.2 或 1.3,但它们却被称为 1,正如 MWE 所演示的那样。如您所见,输出实际上是愚蠢的

我有这些担忧

  1. 我如何将cref子问题标识为“子问题 1.1”?我理想情况下希望发出类似这样的指令\crefname{subquestion}{subquestion}{subquestions}cref调用还应该能够区分普通问题和子问题。
  2. 假设将来我将其更改counter-format = tsk[1]counter-format = tsk[r],我会得到一致的输出吗?类似“子问题 1.a”之类的东西?

由 magguu 编辑::请参阅我的半工作答案和相应的 MWE

请参阅 MWE::

\documentclass[11pt]{article}
\usepackage{tasks,cleveref,xsim}

\NewTasks[counter-format = tsk[1]),
    label-format = \itshape,%
    item-indent = 0em,%
    label-offset=0.8em,%
    label-align=left,%
    before-skip = 0pt,%
    after-item-skip=0pt%
]{subquestionblock}[\subquestion](2)

\crefname{exercise}{question}{questions}
\crefname{subquestion}{subquestion}{subquestions} % has no effect, obviously

\begin{document}
    \begin{exercise}[points=3]
        Isomers of a coordination compound. \label{iso}
        \begin{subquestionblock}
            \subquestion! Why do we have isomers? \label{why}
            \subquestion! Why did I even ask \cref{iso}? \label{metawhy}
            \subquestion! What do you mean \cref{metawhy}?
        \end{subquestionblock}
    \end{exercise}

    \begin{exercise}[points=3]
        Is there an end to stupid questions? \label{stu}
        \begin{subquestionblock}
            \subquestion! Discuss why \cref{metawhy} is a stupid question but \cref{why} is even stupider? \label{huh}
            \subquestion! Are both \cref{huh} and \cref{metawhy} both stupider that each other?
        \end{subquestionblock}
    \end{exercise}
\end{document}

在此处输入图片描述

答案1

向任务包的作者提出您的请求并提交错误报告:

  1. 任务包的代码没有经过适当注释,只是 .sty 文件,因此难以理解。任务包加载/使用的一些包也是如此。

  2. 似乎\refstepcounter该软件包的作者并没有使用,而是基于 expl3 并使用了他的另一个名为 的软件包,自己做了一些相反的事情cntformats,人们不得不花几个小时研究它才能发现可能发生的事情。在我看来,所有这些都让事情变得更加难以捉摸,而且在我看来,如果人们希望自己的东西能够与常见的 LaTeX2e 基础设施正常交互,这绝对不是一个好主意。

  3. 在那个难以捉摸的包中,\cs_gset:Npx用于定义宏\@currentlabel。似乎这确实是全局重新\@currentlabel定义的。LaTeX2e 内核从不全局重新定义\@currentlabel。如果这样做,在结束其中一个tasks环境后放置标签将在引用这些标签时产生错误的数字。

    (嗯,hyperref-package 确实\@currentHref在全球范围内重新定义,在我看来,这是一个类似的根深蒂固的错误概念,因为它可能导致将引用与超链接连接到错误的目标。)

    这种事情确实让那些 LaTeX 初学者感到困惑和害怕,他们小心翼翼地检查结果的正确性,但没有足够的经验来追踪结果不正确时发生了什么。我看到用户在花费数小时和数天研究难以理解的代码而不是学习他们研究领域的相关基础知识后感到绝望,尽管他们对这些事情一无所知,但在老师的敦促下,他们找到了另一种方式来满足他们的个人资料神经质,使用 LaTeX 和特定的软件包,离开学校/大学后,他们再也不需要这些软件包了。

  4. 另外,您还可以使用软件包cleveref。此软件包确实重新定义了一些内核宏,例如,\refstepcounter并且确实依赖于使用标准基础结构进行反向操作的人,而软件包tasks则不会:

    cleveref确实重新定义了\refstepcounter还定义了一个额外的宏\cref@currentlabel,并且确实重新定义了\label将两个条目写入\newlabel.aux 文件而不是一个:通常的\newlabel条目,其数据源自\@currentlabel,以及一个额外的\newlabel条目,其中标签名称具有后缀@cref,其数据源自并提供引用宏\cref@currentlabel所需的额外数据。cleveref

    -packagetasks具有自己的反功能,并且\@currentlabel自行重新定义,而不是使用 LaTeX2e-infrastructure/ \refstepcounter,因此不会设置\cref@currentlabel并破坏该 cleveref包。


灵感来自你自己的答案/你的黑客我刚刚想出了一个变体,你可以去掉右括号,这\cref也使得使用超链接包裹。

当然我的变体不能解决任务包的全局重新定义行为\@currentlabel,但对聪明人\cref@currentlabel

要点是:

我在任务包中添加了另一个键“ugly-label-hook”,它可以让您指定一些标记,只要其中一个项目的任务包发生修改,这些标记就会插入\@currentlabel

您可以使用此钩子为您的子问题/子答案“重新步进”“虚拟”计数器。

“refstepping” “dummy”计数器确保宏\@currentlabel\cref@currentlabel(重新)定义正确。

由于任务包无论出于什么原因都会进行\@currentlabel全局重新定义,因此使用“ugly-label-hook”必须确保\@currentlabel\cref@currentlabel也进行全局(重新)定义。

\documentclass[11pt]{article}
\usepackage{hyperref}
\usepackage{tasks,cleveref,xsim}

% ====Begin of patch of tasks.sty====
\makeatletter
\ExplSyntaxOn

% Allocate another token list for the ugly hack:
\tl_new:N     \l__sillyugly_label_hook_tl

% Add a key "ugly-label-hook" for setting that
% token list:
\keys_define:nn {tasks/list}
  {
    ugly-label-hook    .tl_set:N   =  \l__sillyugly_label_hook_tl ,
  }

% Include the ugly-label-hook-key into the `tasks' object:
%   #1: number of items
%   #2: number of columns
%   #3: label-format
\DeclareObjectType {tasks} {3}
% the `default' template interface:
\DeclareTemplateInterface {tasks} {default} {3}
  {
    enumerate       : boolean   = true    ,
    label           : tokenlist           ,
    indent          : length    = 2.5em   ,
    counter-format  : tokenlist = tsk[a]) ,
    label-format    : tokenlist ,
    %----------------------------------------------------
    ugly-label-hook : tokenlist ,
    %----------------------------------------------------
    label-width     : length    = 1em     ,
    label-offset    : length    = .3333em ,
    item-format     : tokenlist ,
    after-item-skip : skip      = 1ex plus 1ex minus 1ex
  }

% When patching the next macro, the really unlikely marker $ with
% unusual catcode is used in ``$tasks$default$label$'':
\cs_set:Nx \__tasks_restore_dollar:
  { \char_set_catcode:nn {36} { \char_value_catcode:n {36} } }
\char_set_catcode_alignment:N \$

% Include the ugly-label-hook-key into the `default' template code
% and apply the hook when \@currentlabel is changed:
\DeclareTemplateCode {tasks} {default} {3}
  {
    enumerate          = \l__tasks_enumerate_bool           ,
    label              = \l__tasks_label_tl                 ,
    indent             = \l__tasks_item_default_indent_dim  ,
    counter-format     = \l__tasks_label_pattern_tl         ,
    label-format       = \l__tasks_label_format_tl          ,
    %----------------------------------------------------
    ugly-label-hook    = \l__sillyugly_label_hook_tl     ,
    %----------------------------------------------------
    label-width        = \l__tasks_label_default_width_dim  ,
    label-offset       = \l__tasks_label_default_offset_dim ,
    item-format        = \l__tasks_item_format_tl           ,
    after-item-skip    = \l__tasks_after_item_skip
  }
  {
    \AssignTemplateKeys
    \bool_if:NF \l__tasks_label_width_bool
      {
        \dim_set_eq:NN
          \l__tasks_label_width_dim
          \l__tasks_label_default_width_dim
      }
    \bool_if:NF \l__tasks_item_indent_bool
      {
        \dim_set_eq:NN
          \l__tasks_item_indent_dim
          \l__tasks_item_default_indent_dim
      }
    \bool_if:NF \l__tasks_label_offset_bool
      {
        \dim_set_eq:NN
          \l__tasks_label_offset_dim
          \l__tasks_label_default_offset_dim
      }
    % \dim_compare:nNnT
    %   { \l__tasks_item_indent_dim }
    %    <
    %   { \l__tasks_label_offset_dim + \l__tasks_label_width_dim }
    %   {
    %     \dim_set:Nn \l__tasks_item_indent_dim
    %       { \l__tasks_label_offset_dim + \l__tasks_label_width_dim }
    %   }
    \bool_if:NT \l__tasks_custom_after_item_skip_bool
      {
        \skip_set_eq:NN
          \l__tasks_after_item_skip
          \l__tasks_custom_after_item_skip
      }
    \bool_if:NT \l__tasks_custom_label_bool
      {
        \tl_set_eq:NN
          \l__tasks_label_tl
          \l__tasks_custom_label_tl
        \bool_set_false:N \l__tasks_enumerate_bool
      }
    \__tasks_label_align:V \l__tasks_label_align_tl
    % need this for enumerate list:
    \bool_if:nT { !\l__tasks_resume_bool && \l__tasks_enumerate_bool }
      { \int_gzero:N \g__tasks_int }
    \int_set:Nn \l__tasks_columns_int {#2}
    % set all the items in their own coffins and join with the ground:
    \int_gzero:N \g__tasks_current_col_num_int
    \int_set:Nn \g__tasks_current_row_num_int {1}
    \tl_if_blank:VF \l__tasks_custom_label_pattern_tl
      {
        \tl_set_eq:NN
          \l__tasks_label_pattern_tl
          \l__tasks_custom_label_pattern_tl
      }
    \tl_if_blank:VF \l__tasks_custom_label_format_tl
      {
        \tl_set_eq:NN
          \l__tasks_label_format_tl
          \l__tasks_custom_label_format_tl
      }
    \tl_if_blank:VF \l__tasks_custom_item_format_tl
      {
        \tl_set_eq:NN
          \l__tasks_item_format_tl
          \l__tasks_custom_item_format_tl
      }
    \seq_map_inline:Nn \l__tasks_seq
      {
        \__tasks_read_item:www ##1 \q_stop
        \bool_if:NTF \l__tasks_enumerate_bool
          {
            \tl_if_eq:VnT \l__tasks_tmp_label_tl { $tasks$default$label$ }
              {
                \int_gincr:N \g__tasks_int
                \SaveCounterPatternFrom [tasks]
                  \l__tasks_tmpa_tl
                  \l__tasks_label_tl
                  \l__tasks_label_pattern_tl
                %-----------------------------------------------------------
                % This seems to set \@currentlabel globally:
                %-----------------------------------------------------------
                \cs_gset:Npx \@currentlabel { \l__tasks_label_tl }
                %-----------------------------------------------------------
                % Carry out the ugly hook hack after setting \@currentlabel
                % for setting \@currentlabel correctly.
                % Seems with this package this needs to be done globally,
                % thus \uglycounterhack does it globally as well.
                %-----------------------------------------------------------
                \l__sillyugly_label_hook_tl
              }
          }
          {
            \tl_if_blank:VT \l__tasks_label_tl
              { \tl_set_eq:NN \l__tasks_label_tl \labelitemi }
          }
        \tl_put_left:NV \l__tasks_label_tl \l__tasks_label_format_tl
        % \tl_put_left:NV \l__tasks_item_tl \l__tasks_item_format_tl
        \tl_if_eq:VnTF \l__tasks_tmp_label_tl { $tasks$default$label$ }
          {
            \__tasks_task:VVV
              \l__tasks_label_tl
              \l__tasks_item_format_tl
              \l__tasks_item_tl
          }
          {
            \__tasks_task:VVV
              \l__tasks_tmp_label_tl
              \l__tasks_item_format_tl
              \l__tasks_item_tl
            \tl_clear:N \l__tasks_tmp_label_tl
          }
      }
  }

\__tasks_restore_dollar:

\ExplSyntaxOff
\makeatother

% ====End of patch of tasks.sty====

\DeclareExerciseTranslations{answer}{
  Fallback = answer ,
  English = answer ,
  French = r\'eponse ,
  German = Antwort
}


\DeclareExerciseType{question}{
  exercise-env = question ,
  solution-env = answer ,
  exercise-name = \XSIMtranslate{question} ,
  solution-name = \XSIMtranslate{answer} ,
  exercise-template = default ,
  solution-template = default
}

\newcounter{subquestion}[question]
\newcounter{subanswer}[answer]

\renewcommand*\thesubquestion{\thequestion.\arabic{subquestion}}%
\csname @ifpackageloaded\endcsname{hyperref}{%
  \renewcommand*\theHsubquestion{\theHquestion.\arabic{subquestion}}%
}%

\renewcommand*\thesubanswer{\theanswer.\arabic{subanswer}}%
\csname @ifpackageloaded\endcsname{hyperref}{%
  \renewcommand*\theHsubanswer{\theHanswer.\arabic{subanswer}}%
}%


% cleveref-aliases/counter-naming-phrases:
\crefname{question}{question}{questions}
\crefname{subquestion}{subquestion}{subquestions} % has no effect, obviously

\crefname{answer}{answer}{answers}
\crefname{subanswer}{subanswer}{subanswers} % has no effect, obviously

\makeatletter
% Why does the tasks-package its own counter-thingie when
% you have to use a counter anyway for getting things 
% with label..ref done correctly? This does not make sense to me.
\DeclareRobustCommand\uglycounterhack[1]{%
  \refstepcounter{#1}%
  \global\let\@currentlabel=\@currentlabel
  \global\let\cref@currentlabel=\cref@currentlabel
}%
\makeatother

\NewTasks[%
    %% This will not include the value of the question-counter
    %% into the subquestion-items:
    %%   counter-format = tsk[1]),%
    %% This will include the value of the question-counter
    %% into the subquestion-items -- I recommend doing this for making
    %% references to subquestion-items unambiguous:
    counter-format = \thequestion.tsk[1]),%
    label-format = \itshape,%
    ugly-label-hook = \uglycounterhack{subquestion},%
    label-width = 1.75em,%
    item-indent = 2.55em,%
    label-offset=0.8em,%
    label-align=left,%
    before-skip = 0pt,%
    after-item-skip=0pt%
]{subquestionblock}[\subquestion](2)

\NewTasks[%
    %% This will not include the value of the answer-counter
    %% into the subanswer-items:
    %%   counter-format = tsk[1]),%
    %% This will include the value of the answer-counter
    %% into the subanswer-items -- I recommend doing this for making
    %% references to subanswer-items unambiguous:
    counter-format = \theanswer.tsk[1]),%
    label-format = \itshape,%
    ugly-label-hook = \uglycounterhack{subanswer},%
    label-width = 1.75em,%
    item-indent = 2.55em,%
    label-offset=0.8em,%
    label-align=left,%
    before-skip = 0pt,%
    after-item-skip=0pt%
]{subanswerblock}[\subanswer](2)

\begin{document}
    \begin{question}[points=3]
        Isomers of a coordination compound. \label{iso}
        \begin{subquestionblock}
            \subquestion! Why do we have isomers? \label{why}
            \subquestion! Why did I even ask \cref{iso}? \label{metawhy}
            \subquestion! What do you mean \cref{metawhy}?
        \end{subquestionblock}
    \end{question}
    \begin{answer}[print=true]
        \begin{subanswerblock}
            \subanswer! Because isomers are fun. \label{because}
            \subanswer! Because I am funny. \label{metabecause}
            \subanswer! \cref{metabecause} is funny but all in all this is no longer funny. 
        \end{subanswerblock}
    \end{answer}

    \begin{question}[points=3]
        Is there an end to stupid questions? \label{stu}
        \begin{subquestionblock}
            \subquestion! Discuss why \cref{metawhy} is a stupid question but \cref{why} is even stupider? \label{huh}
            \subquestion! Are both \cref{huh} and \cref{metawhy} both stupider that each other?
        \end{subquestionblock}
    \end{question}
\end{document}

在此处输入图片描述

答案2

尝试了几种方法后,我现在有了个丑陋的 hack。它并不能解决所有问题,还引入了一些其他麻烦,但它确实解决了最令人烦恼的计数器问题。为此,我嵌入了exercise一个新环境question,该环境有自己的计数器,称为question。然后 的格式化代码subquestion有一个 hack 来增加另一个称为 的计数器subquestion

最终结果是,我的关注点 2 得到自动解决,如您在附加的输出图像中所见:子问题 3.1 和子问题 3.3。

然而,我仍然无法解决第一个问题。我尝试使用其他一些黑客手段,但陷入了\expandafter混乱。我不会在这里的 MWE 中发布这些努力。

现在我有这些新的担忧:

  1. 如何去掉每个引用后面的“)”?
  2. 我如何将“question”的几个调用更改为“subquestion”。也就是说,1.2 中的“question”应该保持不变,但其他“question”应该更改为“subquestion”。我本可以更改定义,\refsubq但那会带来其他问题。

请参阅新的 MWE::

\documentclass[11pt]{article}
\usepackage{tasks,cleveref,xsim}

\newcommand{\refsubq}[1]{\cref{#1}.\ref{#1}}
\newcounter{question}
\newcounter{subquestion}[question]
\newcommand{\uglyhack}{\refstepcounter{subquestion}\itshape}

\NewTasks[counter-format = tsk[1]),
    label-format = \uglyhack,%
    item-indent = 0em,%
    label-offset=0.8em,%
    label-align=left,%
    before-skip = 0pt,%
    after-item-skip=0pt%
]{subquestionblock}[\subquestion](2)

\NewTasks[counter-format = tsk[a]),
    label-format = \uglyhack,%
    item-indent = 0em,%
    label-offset=0.8em,%
    label-align=left,%
    before-skip = 0pt,%
    after-item-skip=0pt%
]{subquestionblockA}[\subquestion](2)

\newenvironment{question}{\refstepcounter{question}\exercise}{\endexercise}
\crefname{exercise}{question}{questions}

\crefname{subquestion}{subquestion}{subquestions} % has no effect, obviously
\crefname{subquestionblock}{subquestion}{subquestions} % fails
\crefname{subquestionblockA}{subquestion}{subquestions} % fails

\begin{document}
    \begin{question}
        Isomers of a coordination compound. \label{iso}
        \begin{subquestionblock}
            \subquestion! Why do we have isomers? \label{why}
            \subquestion! Why did I even ask \cref{iso}? \label{metawhy}
            \subquestion! What do you mean \refsubq{metawhy}?
        \end{subquestionblock}
    \end{question}

    \begin{question}
        Is there an end to stupid questions? \label{stu}
        \begin{subquestionblockA}
            \subquestion! Discuss why \refsubq{metawhy} is a stupid question but \refsubq{why} is even stupider? \label{huh}
            \subquestion! Are \refsubq{huh} and \refsubq{metawhy} both stupider than each other? \label{huh2}
            \subquestion! Please see \refsubq{goto2} for more info. \label{goto}
        \end{subquestionblockA}
    \end{question}

    \begin{question}
        I am just a question.
        \begin{subquestionblock}
            \subquestion! I am a simple subquestion unlike \refsubq{huh2}.
            \subquestion! I am a subquestion (\refsubq{self}) stupid enough to refer to myself\label{self}.
            \subquestion! Please see \refsubq{goto} for even more info. \label{goto2}
        \end{subquestionblock}
    \end{question}
\end{document}

图像 :: 截屏

相关内容