I have a query with the maximum number of items that can be saved in an expl3
list. Thanks to the response that @egreg gave me in Avoid passing an empty [optional argument] an environment (with verbatim content) using xparse? I was able to finish the implementation of an environment capable of saving valid LaTeX
code (including verbatim
) in expl3
lists ... something like a "ConTextbuffers".
Let me explain a bit, I have many question/answer files in which I have abused the use of filecontents
to record temporary files and included them in environment based on environ
(which did not support verbatim
) and then use them in different parts of the documents.
With the new version of filecontentsdef
it is not necessary to write the external files (included verbatim
) to have them in a \macro
, thus the things and gathering ideas I have managed to create a scontents
environment which encapsulates \filecontentsdefmacro
, what run and save in a seq
list, which I can access with \getstored[index]{seq name}
, the environment and command Scontents
works perfectly.
My question is this, the environment expands the content of \macro
and stores in sequence, the command stores direct in sequence (same or other), at some point the seq
list will have a limit. What is the limit on the number of items I can save?
I have tested some files, but, I convert all my old files using a script
to pass them to the new scontents
environment.
I leave here the example code using l3seq
\documentclass{article}
\usepackage{filecontentsdef}[2019/04/20]
\usepackage{xparse}
\usepackage{pgffor,fvextra} % only for example
\pagestyle{empty} % only for example
\setlength{\parindent}{0pt} % only for example
\ExplSyntaxOn
\tl_new:c { l_scontents_macro_tmp_tl }
\keys_define:nn { scontents }
{
save-env .tl_set:N = \l_scontents_name_seq_tl,
save-env .initial:n = contents,
show-env .bool_set:N = \l_scontents_show_env_bool,
show-env .initial:n = false,
save-cmd .tl_set:N = \l_scontents_name_seq_cmd_tl,
save-cmd .initial:n = contents,
show-cmd .bool_set:N = \l_scontents_show_cmd_bool,
show-cmd .initial:n = false
}
\cs_new_protected:Npn \_scontents_append_contents:nn #1#2
{
\seq_if_exist:cF { g_scontents_seq_name_#1_seq }
{
\seq_new:c { g_scontents_seq_name_#1_seq }
}
\seq_gput_right:cn { g_scontents_seq_name_#1_seq } {#2}
}
\cs_new_protected:Npn \_scontents_getfrom_seq:nn #1#2
{
\seq_item:cn { g_scontents_seq_name_#2_seq } {#1}
}
\ProvideExpandableDocumentCommand{ \countseqsc }{ m }
{
\seq_count:c { g_scontents_seq_name_#1_seq }
}
\keys_define:nn { scontents / Scontents }
{
show-cmd .code:n = { \keys_set:nn { scontents } { show-cmd = #1 } },
save-cmd .code:n = { \keys_set:nn { scontents } { save-cmd = #1 } }
}
\ProvideDocumentCommand{ \Scontents }{ O{} +m }
{
\group_begin:
\IfNoValueF {#1} { \keys_set:nn { scontents / Scontents } {#1} }
\_scontents_append_contents:nn { \l_scontents_name_seq_cmd_tl } { #2 } % add to seq
\bool_if:NT \l_scontents_show_cmd_bool
{
\_scontents_getfrom_seq:nn { -1 }{ \l_scontents_name_seq_cmd_tl }
}
\group_end:
}
\ProvideExpandableDocumentCommand{ \getstored }{ O{1} m }
{
\_scontents_getfrom_seq:nn {#1} {#2}
}
\ProvideDocumentEnvironment{ scontents }{}
{
\char_set_catcode_active:N \^^M
\scontents_start_environment:w
}
{
\scontents_stop_environment:
\scontents_atend_environment:
}
\cs_new_protected:Npn \scontents_environment_keys:w [#1]
{
\keys_set:nn { scontents } {#1}
}
% Star environment
\group_begin:
\char_set_catcode_active:N \^^M
\cs_new_protected:Npn \scontents_start_environment:w #1 ^^M
{
\tl_if_blank:nF {#1} { \scontents_environment_keys:w #1 }
\group_begin: % open group for env
\use:c { filecontentsdefmacro } { \l_scontents_macro_tmp_tl } ^^M
}
\group_end:
% Stop environment
\cs_new_protected:Nn \scontents_stop_environment:
{
\endfilecontentsdefmacro
\group_end: % close group for env
}
% A variant to replace \^^M for \^^J (need by Verb{..} from fvextra)
\cs_generate_variant:Nn \tl_replace_all:Nnn { Nxx }
% Expand \l_tmpa_tl and pass to seq
\cs_gset_protected:Nn \_scontents_macro_to_seq:
{
\regex_replace_all:nnN { \^^M } { \^^J } \l_scontents_macro_tmp_tl
\cs_log:N \l_scontents_macro_tmp_tl
\exp_args:NNx \_scontents_append_contents:nn \l_scontents_name_seq_tl
{
\exp_not:N \scantokens \exp_after:wN { \tl_use:c { l_scontents_macro_tmp_tl} } %
}
}
\cs_new_protected:Nn \scontents_atend_environment:
{
\_scontents_macro_to_seq:
\bool_if:NT \l_scontents_show_env_bool
{
\_scontents_getfrom_seq:nn { -1 }{ \l_scontents_name_seq_tl }
}
\cs_undefine:N \l_scontents_macro_tmp_tl
}
\ExplSyntaxOff
\begin{document}
\section{Test \Verb{\begin{scontents}[key=val]}}
Test \verb+\begin{scontents}+ no \verb+[key=val]+\par
\begin{scontents}
Using \Verb{scontents} env no \verb+[key=val]+, save in seq \verb+contents+ with index 1.\par
\begin{verbatim}
(A) verbatim environment
\end{verbatim}
\end{scontents}
Test \verb+\begin{scontents}[show-env=true]+\par
\begin{scontents}[show-env=true]
Using \verb+scontents+ env with \Verb{[show-env=true]}, save in seq \verb+contents+ with index 2.
We have coded this in \LaTeX: $E=mc^2$.\par
\begin{Verbatim*}
(B) verbatim environment
\end{Verbatim*}
\end{scontents}
Test \verb+\begin{scontents}[show-env=false]+\par
\begin{scontents}[show-env=false]
Using \verb+scontents+ env with \verb+[show-env=false]+, save in seq \verb+contents+ with index 3.
We have coded this in \LaTeX: $E=mc^2$.\par
\begin{verbatim}
(C) verbatim environment
\end{verbatim}
\end{scontents}
Test \verb+\begin{scontents}[show-env=true]+\par
\begin{scontents}[show-env=true]
Using \verb+scontents+ env with \verb+[show-env=true]+, save in seq \verb+contents+ with index 4.
We have coded this in \LaTeX: $E=mc^2$.\par
\begin{verbatim*}
(D) verbatim environment
\end{verbatim*}
\end{scontents}
Test \verb+\Scontents{...}+\par
\Scontents{Using \texttt{Scontents} command (no verbatim), save in seq \texttt{contents} with index 5}
\section{Test \Verb{\getstored[index]{contents}}}
The total contents stored in \verb+contents+ seq are \countseqsc{contents}.\par
\getstored[5]{contents}\par
\getstored{contents}
\section{A simple aplication using \Verb{pgffor} loop}
\newcounter{exeNr}
\newenvironment{exercise}
{\refstepcounter{exeNr}\par\noindent This is exercise~\theexeNr}
{\par}
\subsection{Exercises}
\begin{exercise}
\end{exercise}
\begin{scontents}[save-env = myansewer]
This is the answer to exercise 1, the shebang line for a Perl script
\begin{verbatim}
#!/usr/bin/env perl
\end{verbatim}
\end{scontents}
\begin{exercise}
\end{exercise}
\begin{scontents}[save-env = myansewer]
This is the answer to exercise 2
\end{scontents}
\begin{exercise}
\end{exercise}
\Scontents[save-cmd = myansewer]{This is the answer to exercise 3}
\subsection{Answers}
\newcounter{ansNr}
\newenvironment{answer}
{\refstepcounter{ansNr}\par\noindent Answer~\theansNr:}
{\par}
\foreach \i in {1,...,\countseqsc{myansewer}} {
\begin{answer}
\getstored[\i]{myansewer}
\end{answer}
}
\end{document}
An image of what the output is like:
Annexed query, expl3
provides two modules l3seq
and l3prop
to store data, is there any difference in this specific case?
Regards
expl3
seq variable is a macro deep down, so if you are inserting contents in a seq variable you are basically doing a very large\def
. In this case, your limit will probably be thewords of memory
memory, where in the (my) log there isXX words of memory out of 5000000
. But the amount of items in a seq variable is probably unlimited. The amount you can actually store will depend on the “size” of each element.@
as much as possible and also\scantokens
: you should be aware of\tl_rescan:nn
. The maximum limit of items in a list/sequence is “what can be stored in memory”.@
and the unused code, use\ tl_rescan: nn
instead of\scantokens
not work for me (help at this point appreciated). @egreg or @Phelype Oleinik, if the answer to my question is "what can be stored in memory", please leave it as an answer to not leave the question open. Saludos