Due to the 30 000-character-limit I had to divide this answer into four parts.
This is part 1 of my answer.
Part 1 consists of explanations on how to divide things into different sub-routines and then put together the desired mechanism.
Part 2 consists of several package files/.sty-files which can be loaded via \usepackage/\RequirePackage and which bring along some of the needed sub-routines.
Part 3 also consists of several package files/.sty-files which can be loaded via \usepackage/\RequirePackage and which bring along some of the needed sub-routines.
Part 4 delivers a LaTeX document which via \RequirePackage/\usepackage loads the packages with the needed sub-routines from part 2 and part 3. Within the head of that document, even before the preamble, these sub-routines are used for putting together the desired generic and non-generic user-level-macros.
For testing, save the package files/.sty-files provided in part 2 and part 3 and text.tex from part 4 within the same directory and compile test.tex with a LaTeX-engine.
You can take this question for a nice exercise in implementing things from scratch.
With this exercise you can exhibit useful (La)TeX programming techniques:
\romannumeral0-expansion as a means for triggering expansion until obtaining a desired result/a desired set of tokens.
(I elaborated on \romannumeral0-expansion also in my answer to the question "How can I know the number of expandafters when appending to a csname macro?".)
- Tail-recursion for iterating only by means of (macro-)expansion one after the other on the single elements of lists of comma-delimited arguments/of lists of non-delimited arguments.
- Tail-recursion for accumulating a desired result/a desired set of tokens within a specific argument of a tail-recursive macro.
- Nesting calls to a macro
\UD@PassThirdBeforeFirstToSecond/exchanging macro arguments after having triggered desired expansion as a systematic approach for getting the (changed) arguments of a tail-recursive macro correctly in place within the token-stream before having (La)TeX call/expand the tail-recursive macro in question again.
With the desired command \mychecklist the "set of indices" [1,3,5] forms a list of comma-delimited arguments.
Thus this question includes the task of handling a list of comma-delimited macro arguments.
Extracting an element, e.g., the first element, from a list of comma-delimited arguments (a comma-list) is an interesting task:
Several questions arise. E.g., the question about the treatment of leading and trailing space-tokens with such elements. E.g., the question about the handling of situations where a comma shall not serve as separator between two elements but shall be a component of one such element.
In the package-file UD_ExtractFirstCommaDelimitedArg_2019_09_03.sty I implemented the routine \UD@ExtractFirstCommaArg for extracting the first element of a comma-list in a way where all spaces that might surround the entire element get removed and afterwards one level of curly braces that might surround the entire element gets stripped off also.
This way you can have an entire element surrounded by braces for hiding
- commas that shall not separate elements from each other but shall belong to the element in question itself.
- leading and trailing spaces that shall not be removed from the element in question but shall belong to the element in question.
This approach is somewhat more universal than needed in this scenario. Nevertheless I think its worth presenting it as it can be quite useful in other scenarios.
The comma-list can contain spaces. These will be removed silently: [1 , 3 ,5, 7,9 ,]. The single numbers/indices can be nested in one level of curly braces: [1 , {3} ,5, {7},9 ,]. But, e.g., with [1 , 3 5 , {7},9 ,], the sequence 3 5 will be taken for one element of the comma-list. Due to the space between 3 and 5 that element does not form a digit-sequence/does not form a valid number and except from complaining about it via an error-message LaTeX will ignore it.
A possible workflow for a generic command
\mychecklistwithparameternames{⟨list of comma separated integer numbers in range 1..L⟩}%
{⟨list of L names of the L parameters⟩}%
{⟨name of macro that did call \mychecklistwithparameternames⟩}%
{⟨tokens to insert before the tabularx environment⟩}%
{⟨tokens to insert behind the tabularx environment⟩}%
could be:
⤷ That command expands to:
\DetectAmountOfParametersAndInitFlagsLoop{⟨list of L names of the L parameters (where elements get removed during the loop)⟩}%
{⟨to-be-constructed list of L flags (one flag for each of the L parameters)⟩}% initially empty.
{⟨amount of parameters⟩}% initially 0.
{⟨list of comma separated integer numbers in range 1..L⟩}%
{⟨list of L names of the L parameters (where elements do not get removed so that it can be used after the loop)⟩}%
{⟨name of macro that did call \mychecklistwithparameternames⟩}%
{⟨tokens to insert before the tabularx environment⟩}%
{⟨tokens to insert behind the tabularx environment⟩}%
\DetectAmountOfParametersAndInitFlagsLoop is to detect the ⟨amount of parameters⟩ and to create a ⟨list of L flags (one flag for each of the L parameters)⟩ in terms of a list of non-delimited arguments where each flag is initialized as an argument {0} :
\DetectAmountOfParametersAndInitFlagsLoop via tail-recursion—i.e., via calling itself again with its arguments changed—iterates on the ⟨list of L names of the L parameters (where elements get removed during the loop)⟩ until that list forms a non-delimited macro-argument which is blank ("blank" in this context means that the set of tokens which forms the argument either is empty or does contain space tokens only): Within each iteration remove the first element from that list and to the ⟨to-be-constructed list of L flags (one flag for each of the L parameters)⟩ add another flag-element {0} and increment the ⟨amount of parameters⟩.
( This implies that \DetectAmountOfParametersAndInitFlagsLoop needs sub-routines for the following tasks:
- Checking whether a macro argument is blank.
In the example test.tex from part 4 this is the routine \UD@CheckWhetherBlank from UD_Paraphernalia_2019_09_03.sty.
- Incrementing an integer number by 1.
In the example test.tex from part 4 this is the routine \UD@Increment from UD_Increment_2019_09_03.sty.
- Removing an element from a list of non-delimited arguments.
In the example test.tex from part 4 this is done via \UD@FirstOfTwo{}⟨list of non-delimited arguments⟩. )
When that recursive iteration is done, i.e., when the ⟨list of L names of the L parameters (where elements get removed during the loop)⟩ is a blank non-delimited macro-argument, then terminate the \DetectAmountOfParametersAndInitFlagsLoop-tail-recursion and have another tail-recursive macro called, \SetFlagsNWarningsLoop:
⤷
\SetFlagsNWarningsLoop{⟨list of comma separated integer numbers in range 1..L⟩}%
{⟨list of L flags (one flag for each of the L parameters)⟩}% all now initialized "{0}".
{⟨list of warnings⟩}% initially empty.
{⟨amount of parameters⟩}%
{⟨list of L names of the L parameters⟩}%
{⟨name of macro that did call \mychecklistwithparameternames⟩}%
{⟨tokens to insert before the tabularx environment⟩}%
{⟨tokens to insert behind the tabularx environment⟩}%
\SetFlagsNWarningsLoop via tail-recursion iterates on the ⟨list of comma separated integer numbers in range 1..L⟩ for changing those flags in the ⟨List of L flags (one flag for each of the L parameters)⟩ to {1} whose numbers occur in the ⟨list of comma separated integer numbers in range 1..L⟩ :
As long as the ⟨list of comma separated integer numbers in range 1..L⟩ is not blank have \SetFlagsNWarningsLoop call itself again after having changed (and via nested-\UD@PassThirdBeforeFirstToSecond-technique having brought in place) its arguments as follows:
- If the first element of the
⟨list of comma separated integer numbers in range 1..L⟩ is empty
, then do nothing
, else
- if the first element of the
⟨list of comma separated integer numbers in range 1..L⟩ can be taken for a positive integer number K with 1 ≤ K ≤ ⟨amount of parameters⟩
- , then replace the K-th element of the
⟨list of L flags (one for flag each of the L parameters)⟩ by the element {1}
- , else add an entry to the
⟨list of warnings⟩.
- Remove the first element of the
⟨list of comma separated integer numbers in range 1..L⟩.
When the ⟨list of comma separated integer numbers in range 1..L⟩ is blank, then terminate the \SetFlagsNWarningsLoop-tail-recursion by calling \TableRowsLoop.
( This implies that \SetFlagsNWarningsLoop needs sub-routines for the following tasks:
- Checking whether a macro argument is empty.
In the example test.tex from part 4 this is the routine \UD@CheckWhetherNull from UD_Paraphernalia_2019_09_03.sty.
- Checking whether a macro argument is blank.
In the example test.tex from part 4 this is the routine \UD@CheckWhetherBlank from [UD_Paraphernalia_2019_09_03.sty].
- Extracting the first element of a comma separated list.
In the example test.tex from part 4 this is the routine \UD@ExtractFirstCommaArg from UD_ExtractFirstCommaDelimitedArg_2019_09_03.sty.
- Removing the first element from a comma separated list.
In the example test.tex from part 4 this is the macro \UD@GobbleToComma from UD_ExtractFirstCommaDelimitedArg_2019_09_03.sty.
- Checking whether an argument forms a positive integer within a specified range.
In the example test.tex from part 4 this is the routine \UD@CheckWhetherDigitTokensInRangeOnly from UD_NumCheck_2019_09_03.sty.
- Replacing the K-th element of a list of non-delimited arguments by something else.
In the example test.tex from part 4 this is the routine \UD@ReplaceKthArg from UD_ReplaceKthUndelimited_2019_09_03.sty.
- Raising a warning-message.
In the example test.tex from part 4 this is the routine \UD@NotANumberInValidRangeError from UD_NumCheck_2019_09_03.sty. )
\TableRowsLoop also is tail-recursive and needs to be called as follows:
⤷
\TableRowsLoop{⟨list of L flags (one flag for each of the L parameters)⟩}%
{⟨list of L names of the L parameters⟩}%
{⟨table-rows constructed so far⟩}% initially empty.
{⟨list of warnings⟩}%
{⟨tokens to insert before the tabular xenvironment⟩}%
{⟨tokens to insert behind the tabularx environment⟩}%
\TableRowsLoop via tail-recursion iterates on the ⟨list of L flags (one flag for each of the L parameters)⟩ and on
the ⟨list of L names of the L parameters⟩ and creates the table rows:
When ⟨list of L flags (one flag for each of the L parameters)⟩ is empty
, then
- terminate the tail-recursive loop,
- "spit out":
- the
⟨tokens to insert before the tabularx environment⟩,
- the
⟨table-rows constructed so far⟩, nested inside a table- and a tabular-environment,
- the
⟨tokens to insert behind the tabularx environment⟩,
- the
⟨list of warnings⟩
, else have \TableRowsLoop call itself again after having changed (and via nested-\UD@PassThirdBeforeFirstToSecond-technique having brought in place) its arguments as follows:
- If the
⟨list of L flags (one flag for each of the L parameters)⟩
contains only one element (last element)
- , then:
If the first element of the ⟨list of L flags (one flag for each of the L parameters)⟩ denotes the number 0
- , then add a sequence
⟨first element of the ⟨list of L names of the L parameters⟩⟩&
to the ⟨table-rows constructed so far⟩
- , else add a sequence
⟨first element of the ⟨list of L names of the L parameters⟩&x
to the ⟨table-rows constructed so far⟩.
- , else:
If the first element of the ⟨list of L flags (one flag for each of the L parameters)⟩ denotes the number 0
- , then add a sequence
⟨first element of the ⟨list of L names of the L parameters⟩⟩&\\
to the ⟨table-rows constructed so far⟩
- , else add a sequence
⟨first element of the ⟨list of L names of the L parameters⟩&x\\
to the ⟨table-rows constructed so far⟩.
- Remove the first element of the
⟨list of L flags (one flag for each of the L parameters)⟩.
- Remove the first element of the
⟨list of L names of the L parameters⟩.
( This implies that \TableRowsLoop needs sub-routines for the following tasks:
- Checking whether a non-delimited macro argument is empty.
In the example test.tex from part 4 this is the routine \UD@CheckWhetherNull from UD_Paraphernalia_2019_09_03.sty.
- Extracting the first element of a list of non-delimited arguments.
In the example test.tex from part 4 this is the routine \UD@ExtractFirstArg from UD_ExtractFirstUndelimitedArg_2019_09_03.sty.
- Checking whether a macro argument denotes the number "0".
In the example test.tex from part 4 the routine \UD@CheckWhetherDigitTokensInRangeOnly from UD_NumCheck_2019_09_03.sty is used for this.
- Removing an element from a list of non-delimited arguments.
In the example test.tex from part 4 this is done via \UD@FirstOfTwo{}⟨list of non-delimited arguments⟩. )
The sub-routines provided in the package files, except \UD@NotANumberInValidRangeError from UD_NumCheck_2019_09_03.sty, due to \romannumeral0-expansion deliver their results after two expansion-steps/after two "hits" by \expandafter.
Thus with the code in the example test.tex from part 4 applying the nested-\UD@PassThirdBeforeFirstToSecond-technique for bringing arguments in place for calling the next loop-instance of a tail-recursive macro often needs to be combined with having \UD@PassThirdBeforeFirstToSecond's first argument "hit" by \expandafter twice before performing the exchange.
That's why in UD_Paraphernalia_2019_09_03.sty I implemented a helper-macro \UD@PassThirdBeforeTwiceExpandedFirstToSecond.
Based on the generic command \mychecklistwithparameternames you could define \mychecklist with syntax:
\mychecklist[⟨comma list⟩]%
{⟨caption of the table⟩}%
{⟨referencing label of the table⟩}
like this:
\newcommand\mychecklist[3][⟨comma list with defaults⟩]{%
\mychecklistwithparameternames{#1}%
{{param 1}{param 2}..{param L}}%
{mychecklist}%
{\caption{#2}\label{#3}}% ← We want the caption before the tabular environment
{}%
}%
(In the example test.tex from part 4 not just \caption and \label will be delivered but it will be tested whether #2 (⟨caption⟩) and #3 (⟨label⟩) are empty and in case ⟨caption⟩ is empty while ⟨label⟩ is not, a warning-message will be delivered as ususally placing a referencing-label without a caption/without a sectioning command does not make much sense.)
Due to the 30 000-character-limit I had to divide this answer into four parts.
This is part 1 of my answer.
Part 1 consists of explanations on how to divide things into different sub-routines and then put together the desired mechanism.
Part 2 consists of several package files/.sty-files which can be loaded via \usepackage/\RequirePackage and which bring along some of the needed sub-routines.
Part 3 also consists of several package files/.sty-files which can be loaded via \usepackage/\RequirePackage and which bring along some of the needed sub-routines.
Part 4 delivers a LaTeX document which via \RequirePackage/\usepackage loads the packages with the needed sub-routines from part 2 and part 3. Within the head of that document, even before the preamble, these sub-routines are used for putting together the desired generic and non-generic user-level-macros.
For testing, save the package files/.sty-files provided in part 2 and part 3 and text.tex from part 4 within the same directory and compile test.tex with a LaTeX-engine.
\mychecklist{1,3,5,6,7,8,9,1,2,3,4,5,6,7}there are several macros for iterating through a comma list – David Carlisle Jul 02 '19 at 13:28tabularxwith a preamble ofll– David Carlisle Jul 02 '19 at 13:38