![使用 xparse 迭代列表](https://linux22.com/image/466721/%E4%BD%BF%E7%94%A8%20xparse%20%E8%BF%AD%E4%BB%A3%E5%88%97%E8%A1%A8.png)
我有这个xparse
带包的代码
\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\NewDocumentCommand{\addexercise}{m m m}
{
\prop_gput:Nnn \g_exercise{ #1 } { {#2} {#3} }
}
\DeclareExpandableDocumentCommand{\Question}{m}
{
\prop_get:NnNTF \g_exercise { #1 } \l_tmpa_tl
{\tl_item:Nn \l_tmpa_tl { 1 }}
}
\DeclareExpandableDocumentCommand{\Solution}{m}
{
\prop_get:NnNTF \g_exercise { #1 } \l_tmpa_tl
{\tl_item:Nn \l_tmpa_tl { 2 }}
}
\prop_new:N \g_exercise
\ExplSyntaxOff
\addexercise
{01}
{
\textbf{Question 1}
}
{
\textbf{Solution 1}
}
\addexercise
{02}
{
\textbf{Question 2}
}
{
\textbf{Solution 2}
}
\begin{document}
\section*{Exam}
\Question{01}
\\
\\
\Solution{01}
\\
\\
\\
\Question{02}
\\
\\
\Solution{02}
\end{document}
我怎样才能用类似循环的方法遍历所有问题?问题的数量是可变的。所以我需要计算它。
谢谢
答案1
如果您按顺序存储键并循环遍历此键,则可以按输入顺序循环遍历属性列表。我还建议将问题和解决方案存储为不同的属性。
您仍然可以打印选定的练习和解决方案。
建议使用\NewDocumentCommand
来解析参数并将其传递给内部函数。这样,您就可以以多种方式使用内部函数,如下面的代码所示。
\documentclass{article}
\ExplSyntaxOn
\NewDocumentCommand{\addexercise}{m m m}
{
\exercise_add:nnn { #1 } { #2 } { #3 }
}
\NewDocumentCommand{\Question}{m}
{
\exercise_question:n { #1 }
}
\NewDocumentCommand{\Solution}{m}
{
\exercise_solution:n { #1 }
}
\NewDocumentCommand{\printexercises}{}
{
\exercise_print:
}
\prop_new:N \g_exercise_texts_prop
\seq_new:N \g_exercise_keys_seq
\cs_new_protected:Nn \exercise_add:nnn
{
\seq_gput_right:Nn \g_exercise_keys_seq { #1 }
\prop_gput:Nnn \g_exercise_texts_prop { #1@text } { #2 }
\prop_gput:Nnn \g_exercise_texts_prop { #1@sol } { #3 }
}
\cs_new_protected:Nn \exercise_question:n
{
\prop_get:NnNT \g_exercise_texts_prop { #1@text } \l_tmpa_tl
{ \tl_use:N \l_tmpa_tl }
}
\cs_new_protected:Nn \exercise_solution:n
{
\prop_get:NnNT \g_exercise_texts_prop { #1@sol } \l_tmpa_tl
{ \tl_use:N \l_tmpa_tl }
}
\cs_new_protected:Nn \exercise_print:
{
\seq_map_function:NN \g_exercise_keys_seq \__exercise_print:n
}
\cs_new_protected:Nn \__exercise_print:n
{
\noindent
\exercise_question:n { #1 } \par
\noindent
\exercise_solution:n { #1 } \par
\addvspace{\topsep}
}
\ExplSyntaxOff
\addexercise{01}
{\textbf{Question 1}}
{\textbf{Solution 1}}
\addexercise{02}
{\textbf{Question 2}}
{\textbf{Solution 2}}
\begin{document}
\section*{Exam}
\printexercises
\end{document}
只需稍加改动,您还可以选择是否仅打印问题还是也打印解决方案。
\documentclass{article}
\ExplSyntaxOn
\NewDocumentCommand{\addexercise}{m m m}
{
\exercise_add:nnn { #1 } { #2 } { #3 }
}
\NewDocumentCommand{\Question}{m}
{
\exercise_question:n { #1 }
}
\NewDocumentCommand{\Solution}{m}
{
\exercise_solution:n { #1 }
}
\NewDocumentCommand{\printexercises}{s}
{
\IfBooleanTF{#1}
{
\bool_gset_true:N \g_exercise_showsol_bool
}
{
\bool_gset_false:N \g_exercise_showsol_bool
}
\exercise_print:
}
\prop_new:N \g_exercise_texts_prop
\seq_new:N \g_exercise_keys_seq
\bool_new:N \g_exercise_showsol_bool
\cs_new_protected:Nn \exercise_add:nnn
{
\seq_gput_right:Nn \g_exercise_keys_seq { #1 }
\prop_gput:Nnn \g_exercise_texts_prop { #1@text } { #2 }
\prop_gput:Nnn \g_exercise_texts_prop { #1@sol } { #3 }
}
\cs_new_protected:Nn \exercise_question:n
{
\prop_get:NnNT \g_exercise_texts_prop { #1@text } \l_tmpa_tl
{ \tl_use:N \l_tmpa_tl }
}
\cs_new_protected:Nn \exercise_solution:n
{
\prop_get:NnNT \g_exercise_texts_prop { #1@sol } \l_tmpa_tl
{ \tl_use:N \l_tmpa_tl }
}
\cs_new_protected:Nn \exercise_print:
{
\seq_map_function:NN \g_exercise_keys_seq \__exercise_print:n
}
\cs_new_protected:Nn \__exercise_print:n
{
\noindent
\exercise_question:n { #1 } \par
\bool_if:NT \g_exercise_showsol_bool
{
\noindent
\exercise_solution:n { #1 } \par
}
\addvspace{\topsep}
}
\ExplSyntaxOff
\addexercise{01}
{\textbf{Question 1}}
{\textbf{Solution 1}}
\addexercise{02}
{\textbf{Question 2}}
{\textbf{Solution 2}}
\addexercise{03}
{\textbf{Question 3}}
{\textbf{Solution 3}}
\addexercise{04}
{\textbf{Question 4}}
{\textbf{Solution 4}}
\begin{document}
\section*{Exam}
\printexercises
\section*{Exam with solutions}
\printexercises*
\end{document}
最后几点说明。该函数需要五个参数,但您只提供了四个。如果您不想在密钥不存在时执行特殊操作,\prop_get:NnNTF
请省略。F
也\DeclareExpandableDocumentCommand
最好是\NewExpandableDocumentCommand
,但为此使用的内部函数必须全部可扩展,而\prop_get:NnN(TF)
并非如此。所以你必须使用\NewDocumentCommand
它。
我根据指南重命名了变量:\g_exercise
例如,是一个坏名字。
xparse
当前的 LaTeX 不再需要加载。
答案2
您可以使用\prop_map_inline:Nn
(或\prop_map_function:NN
) 来循环遍历属性列表。但请注意,属性列表是无序的,因此您不能指望获得的顺序与您填写属性列表时的顺序相同。
\prop_map_inline:Nn
您可以在里面使用#1
它来访问属性的键并#2
访问其值。
另请注意,您的变量应遵循expl3
命名约定\<scope>_<module>_<name>_<type>
,在您的情况下类似于\g_user_exercise_prop
。
另请注意,您的\Question
和\Answer
不可扩展,因此不应使用\DeclareExpandableDocumentCommand
。
此外,虽然以下内容仍可用于\\
从 MWE 获取文档部分的换行符,但这并不是在 LaTeX 中获取大量换行符的好方法,您应该\\
很少使用(经验法则:仅在表格中获取下一行)。相反,我的打印例程使用\par
。
\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\prop_new:N \g_user_exercise_prop
\msg_new:nnn {user} {unknown-exercise}
{ No~ exercise~ with~ the~ name~ #1~ defined. }
\NewDocumentCommand{\addexercise}{m m m}
{
\prop_gput:Nnn \g_user_exercise_prop {#1} { {#2} {#3} }
}
\cs_new_protected:Npn \__user_get_exercise:Nn #1#2
{
\prop_get:NnNTF \g_user_exercise_prop {#2} \l_tmpa_tl
{ \exp_after:wN #1 \l_tmpa_tl }
{ \msg_error:nnn {user} {unknown-exercise} {#2} }
}
\NewDocumentCommand{\Question}{m}
{ \__user_get_exercise:Nn \use_i:nn {#1} }
\NewDocumentCommand{\Solution}{m}
{ \__user_get_exercise:Nn \use_ii:nn {#1} }
\NewExpandableDocumentCommand\UseQuestion {m} { \use_i:nn #1 }
\NewExpandableDocumentCommand\UseSolution {m} { \use_ii:nn #1 }
\NewDocumentCommand\MapExercises{+m}
{ \prop_map_inline:Nn \g_user_exercise_prop {#1} }
\ExplSyntaxOff
\addexercise{01}
{\textbf{Question 1}}
{\textbf{Solution 1}}
\addexercise{02}
{\textbf{Question 2}}
{\textbf{Solution 2}}
\begin{document}
\section*{Exam}
\MapExercises{\noindent\UseQuestion{#2}\par\noindent\UseSolution{#2}\par}
\Question{01}
\\
\\
\Solution{01}
\\
\\
\\
\Question{02}
\\
\\
\Solution{02}
\end{document}