
Basado en elLa respuesta de Wipet., y con el mismo espíritu de aprendizaje, sobre el bucle iterativo "for", es interesante descubrir cómo el "bucle for" propuesto podría ser más modular. Quiero pasar, como argumento, la lista iterable propuesta en el ejemplo anterior.
\documentclass[a4paper]{article}
\usepackage{pst-solides3d}
%%% FOR LOOP %%%
\makeatletter
\long\def\for#1in#2#3{\expandafter\def\csname b:\string#1\endcsname{#3}%
\@forinA#1#2;}
\long\def\@forinA#1#2;{\ifx#2\else
\def#1{#2}\csname b:\string#1\endcsname \expandafter\@forinA\expandafter#1\fi}
%%%%%%%%%%%%%%%%%%
\makeatother
%% Variables%%
\pgfmathparse{1.2}\edef\linkLength{\pgfmathresult}
\pgfmathparse{0.2}\edef\jointRadio{\pgfmathresult}
\pgfmathparse{0.6}\edef\jointLength{\pgfmathresult}
\newcommand{\iterating}{
%%% point's structure %%%
\for\i in {%
0 0 0;% N°0
0 0 0;% N°1
0 0 \linkLength;% N°2
0 0 \jointLength;% N°3
0 {\jointLength*0.5} {\linkLength + \jointRadio};% N°4
}
{%
\i\par
%... call to several macros
}%
}
\begin{document}
\iterating
\end{document}
El comportamiento del código es correcto para mi propósito.
0 0 0
0 0 0
0 0 1.2
0 0 0.6
0 0.6*0.5 1.2+ 0.2
Esta lista, es una lista implícita, con varias variables y operaciones aritméticas en cada componente. Entonces, la idea principal es pasar la lista como una macro, algo así como \newcommand{\list}{0 0 0;0 0 0;0 0 \linkLength;0 0 \jointLength;0 {\jointLength*0.5} {\linkLength + \jointRadio}}
llamarla como \for\i in \list
. No entiendo qué modificaciones serán necesarias para que la declaración alcance este comportamiento. (Me doy cuenta de que este comportamiento se puede lograr usando la declaración \foreach pero necesito entender cómo se puede hacer)
Cualquier idea será bien recibida.
Respuesta1
(La macro \list
ya está definida y en uso para el list
entorno en LaTeX 2e.
Por lo tanto, en el siguiente ejemplo, la macro \MyList
está definida y utilizada en su lugar).
Puede, por ejemplo, convertir fácilmente el asunto en una cuestión de expandir la macro que contiene la lista y luego intercambiar argumentos.
Con el siguiente ejemplo, \expandafter
se utiliza para expandir la macro \MyList
(cuya expansión produce la lista que se va a iterar) cronológicamente antes de \PassFirstToSecond
mover esa lista (anidada entre llaves) dentro de la disposición formada por el flujo de tokens detrás de la \for\i in
construcción.
\documentclass[a4paper]{article}
\usepackage{pst-solides3d}
%%% FOR LOOP %%%
\makeatletter
\long\def\for#1in#2#3{\expandafter\def\csname b:\string#1\endcsname{#3}%
\@forinA#1#2;}
\long\def\@forinA#1#2;{\ifx#2\else
\def#1{#2}\csname b:\string#1\endcsname \expandafter\@forinA\expandafter#1\fi}
%%%%%%%%%%%%%%%%%%
\makeatother
%% Variables%%
\pgfmathparse{1.2}\edef\linkLength{\pgfmathresult}
\pgfmathparse{0.2}\edef\jointRadio{\pgfmathresult}
\pgfmathparse{0.6}\edef\jointLength{\pgfmathresult}
\newcommand{\iterating}{%%%%%%%%%%%%%
%%% point's structure %%%
\for\i in {%
0 0 0;% N°0
0 0 0;% N°1
0 0 \linkLength;% N°2
0 0 \jointLength;% N°3
0 {\jointLength*0.5} {\linkLength + \jointRadio};% N°4
}
{%
\i\par
%... call to several macros
}%
}
\newcommand\PassFirstToSecond[2]{#2{#1}}
\newcommand\MyList{%
0 0 0;% N°0
0 0 0;% N°1
0 0 \linkLength;% N°2
0 0 \jointLength;% N°3
0 {\jointLength*0.5} {\linkLength + \jointRadio};% N°4
}%
\begin{document}
\noindent\verb|\iterating| yields:
\iterating
\bigskip
\noindent\verb|\expandafter\PassFirstToSecond\expandafter{\MyList}{\for\i in }{\i\par}| yields:
\expandafter\PassFirstToSecond\expandafter{\MyList}{\for\i in }{\i\par}%
\bigskip
\noindent You can also do another kind of \verb|\expandafter|-orgy:
\bigskip
\noindent\verb|\expandafter\for\expandafter\i\expandafter i\expandafter n\expandafter{\MyList}{\i\par}| yields:
\expandafter\for\expandafter\i\expandafter i\expandafter n\expandafter{\MyList}{\i\par}%
\end{document}
También puede utilizar una macro como \romannumeral\Expandtimes
para especificar el nivel de expansión que se necesita con el argumento que contiene la lista; \romannumeral\Expandtimes
se explica en¿Cómo puedo saber el número de \expandafter
mensajes de correo electrónico al agregar a una macro csname?:
\documentclass[a4paper]{article}
\usepackage{pst-solides3d}
%%% FOR LOOP %%%
\makeatletter
%
\newcommand\exchange[2]{#2#1}%
% A check is needed for finding out if an argument is catcode-11-"d" while there are only
% the possibilities that the argument is either a single catcode-11-"d"
% or a single catcode-12-"m":
\def\innerdfork#1d#2#3dd{#2}%
\def\dfork#1{\innerdfork#1{\@firstoftwo}d{\@secondoftwo}dd}%
% By means of \romannumeral create as many catcode-12-characters m as expansion-steps are to take place.
% Then by means of recursion for each of these m double the amount of `\expandafter`-tokens and
% add one `\expandafter`-token within \innerExp's first argument.
\def\Expandtimes#1{0\expandafter\innerExp\expandafter{\expandafter}\romannumeral\number\number#1 000d}
\def\innerExp#1#2{\dfork{#2}{#1 }{\innerExp{#1#1\expandafter}}}
\long\def\for#1in #2-level-expansion of #3#4{%
\expandafter\def\csname b:\string#1\endcsname{#4}%
\expandafter\exchange\expandafter{\romannumeral\Expandtimes{#2}#3;}{\@forinA#1}%
}
\long\def\@forinA#1#2;{\ifx#2\else
\def#1{#2}\csname b:\string#1\endcsname \expandafter\@forinA\expandafter#1\fi}
%%%%%%%%%%%%%%%%%%
\makeatother
%% Variables%%
\pgfmathparse{1.2}\edef\linkLength{\pgfmathresult}
\pgfmathparse{0.2}\edef\jointRadio{\pgfmathresult}
\pgfmathparse{0.6}\edef\jointLength{\pgfmathresult}
\newcommand\MyList{%
0 0 0;%
0 0 0;%
0 0 \linkLength;%
0 0 \jointLength;%
0 {\jointLength*0.5} {\linkLength + \jointRadio};%
}%
\newcommand\MyOuterListContainer{\MyInnerListContainer}
\newcommand\MyInnerListContainer{\MyList}
\begin{document}
\begingroup
\topsep=0ex \partopsep=0ex
\begin{verbatim}
\for\i in 0-level-expansion of {%
0 0 0;%
0 0 0;%
0 0 \linkLength;%
0 0 \jointLength;%
0 {\jointLength*0.5} {\linkLength + \jointRadio};%
}{\i\par}%
\end{verbatim}%
\smallskip
\endgroup
\noindent yields:
\for\i in 0-level-expansion of {%
0 0 0;%
0 0 0;%
0 0 \linkLength;%
0 0 \jointLength;%
0 {\jointLength*0.5} {\linkLength + \jointRadio};%
}{\i\par}%
\bigskip
\noindent\verb|\for\i in 1-level-expansion of {\MyList}{\i\par}| yields:
\for\i in 1-level-expansion of {\MyList}{\i\par}
\bigskip
\noindent\verb|\for\i in 3-level-expansion of {\MyOuterListContainer}{\i\par}| yields:
\for\i in 3-level-expansion of {\MyOuterListContainer}{\i\par}
\end{document}
Respuesta2
\documentclass[a4paper]{article}
\usepackage[T1]{fontenc}
\usepackage{pst-solides3d}
%% Variables%%
\pgfmathparse{1.2}\edef\linkLength{\pgfmathresult}
\pgfmathparse{0.2}\edef\jointRadio{\pgfmathresult}
\pgfmathparse{0.6}\edef\jointLength{\pgfmathresult}
\newcommand\itlist{
0 0 0;% N°0
0 0 0;% N°1
0 0 \linkLength;% N°2
0 0 \jointLength;% N°3
0 {\jointLength*0.5} {\linkLength + \jointRadio};% N°4
}
\usepackage{listofitems}
\begin{document}
\setsepchar{;/ }
\ignoreemptyitems
\readlist*\mylist{\itlist}
Iterate by row:
\foreachitem\x\in\mylist[]{\par%
\fbox{\x}
}
Third item on 5th row (the actual tokens):\\
\detokenize\expandafter\expandafter\expandafter{\mylist[5,3]}\\
which expands to \mylist[5,3]
1st, 3rd items on 3rd row, in a box is \fbox{\mylist[3,2],\mylist[3,3]}
Now to iterate on each item:
\foreachitem\x\in\mylist{\par%
\foreachitem\y\in\mylist[\xcnt]{%
\fbox{\y}
}}
\end{document}
Respuesta3
Aquí hay un código que estoy desarrollando por placer. Guarde el siguiente código comoextforeach-code.tex
% extforeach-code.tex
\ExplSyntaxOn
\providecommand\fpeval{\fp_eval:n}
\NewDocumentCommand{\nforeach}{ m +m }
{
\tl_clear:N \l__manual_nforeach_type_tl
\keys_set:nn { manual/nforeach }
{
type=integers,start = 1, step = 1, end = 0,
}
\keys_set:nn { manual/nforeach } { #1 }
\__manual_nforeach_exec:n { #2 }
}
\int_new:N \g__manual_foreach_map_int
\int_new:N \g__manual_fp_map_int
\tl_new:N \l__manual_nforeach_type_tl
\keys_define:nn { manual/nforeach }
{
type .choice:,
type .value_required:n = true,
type/integers .code:n = \tl_set:Nn \l__manual_nforeach_type_tl { integers },
type/fp .code:n = \tl_set:Nn \l__manual_nforeach_type_tl { fp },
type/alph .code:n = \tl_set:Nn \l__manual_nforeach_type_tl { alph },
type/Alph .code:n = \tl_set:Nn \l__manual_nforeach_type_tl { Alph },
start .tl_set:N = \l__manual_nforeach_start_tl,
step .tl_set:N = \l__manual_nforeach_step_tl,
end .tl_set:N = \l__manual_nforeach_end_tl,
}
\cs_new_protected:Nn \__manual_nforeach_exec:n
{
\int_gincr:N \g__manual_foreach_map_int
\str_case:Vn \l__manual_nforeach_type_tl
{
{integers}{\__manual_nforeach_exec_integers:n { #1 }}
{fp} {\__manual_nforeach_exec_fp:n { #1 }}
{alph} {\__manual_nforeach_exec_alph:Nn \int_to_alph:n { #1 }}
{Alph} {\__manual_nforeach_exec_alph:Nn \int_to_Alph:n { #1 }}
}
\int_gdecr:N \g__manual_foreach_map_int
}
\cs_generate_variant:Nn \str_case:nn { V }
\cs_new_protected:Nn \__manual_nforeach_exec_integers:n
{
\int_step_inline:nnnn
{ \l__manual_nforeach_start_tl }
{ \l__manual_nforeach_step_tl }
{ \l__manual_nforeach_end_tl }
{ #1 }
}
\cs_new_protected:Nn \__manual_nforeach_exec_alph:Nn
{
\cs_set:cn { __manual_nforeach_alph_ \int_use:N \g__manual_foreach_map_int :n } { #2 }
\cs_generate_variant:cn
{ __manual_nforeach_alph_ \int_use:N \g__manual_foreach_map_int :n }
{ f }
\int_step_inline:nnnn
{ \int_from_alph:f { \l__manual_nforeach_start_tl } }
{ \l__manual_nforeach_step_tl }
{ \int_from_alph:f { \l__manual_nforeach_end_tl } }
{
\use:c { __manual_nforeach_alph_ \int_use:N \g__manual_foreach_map_int :f }
{ #1 { ##1 } }
}
}
\cs_generate_variant:Nn \cs_generate_variant:Nn { c }
\cs_generate_variant:Nn \int_from_alph:n { f }
\cs_new_protected:Nn \__manual_nforeach_exec_fp:n
{
\fp_step_inline:nnnn
{ \l__manual_nforeach_start_tl }
{ \l__manual_nforeach_step_tl }
{ \l__manual_nforeach_end_tl }
{ #1 }
}
\NewDocumentCommand{\lforeach}{ s O{} m +m }
{
\IfBooleanTF{#1}
{
\manual_lforeach:non { #2 } { #3 } { #4 }
}
{
\manual_lforeach:nnn { #2 } { #3 } { #4 }
}
}
\cs_new_protected:Nn \manual_lforeach:nnn
{
\keys_set:nn { manual/lforeach } { single }
\keys_set:nn { manual/lforeach } { #1 }
\clist_set:Nn \l__manual_lforeach_list_clist { #2 }
\int_gincr:N \g__manual_foreach_map_int
\__manual_lforeach_define:n { #3 }
\clist_map_inline:Nn \l__manual_lforeach_list_clist
{
\use:c { __manual_lforeach_ \int_use:N \g__manual_foreach_map_int _action:w } ##1 \q_stop
}
\int_gdecr:N \g__manual_foreach_map_int
}
\cs_generate_variant:Nn \manual_lforeach:nnn { no }
\cs_new_protected:Nn \__manual_lforeach_define:n
{
\exp_last_unbraced:NcV
\cs_set:Npn
{ __manual_lforeach_ \int_use:N \g__manual_foreach_map_int _action:w }
\l__manual_lforeach_format_tl
\q_stop
{#1}
}
\keys_define:nn { manual/lforeach }
{
format .tl_set:N = \l__manual_lforeach_format_tl,
single .code:n = \tl_set:Nn \l__manual_lforeach_format_tl { ##1 },
double .code:n = \tl_set:Nn \l__manual_lforeach_format_tl { ##1/##2 },
triple .code:n = \tl_set:Nn \l__manual_lforeach_format_tl { ##1/##2/##3 },
}
\ExplSyntaxOff
Ahora tu documento puede ser
\documentclass{article}
\usepackage{xparse,xfp}
\input{extforeach-code.tex}
\newcommand\linkLength{1.2}
\newcommand\jointRadio{0.2}
\newcommand\jointLength{0.6}
\newcounter{lines}
\begin{document}
\lforeach[format=#1 #2 #3]{
0 0 0, % N°0
0 0 0, % N°1
0 0 \linkLength, % N°2
0 0 \jointLength, % N°3
0 {\jointLength*0.5} {\linkLength + \jointRadio}, % N°4
}
{%
\stepcounter{lines}Line \thelines\ is \fpeval{#1}~\fpeval{#2}~\fpeval{#3}\par
}
\end{document}
Con la ayuda de xfp
podemos incluso evaluar expresiones.
La format
clave configura una plantilla para cada elemento en la lista separada por comas dada como primer argumento obligatorio, aquí #1 #2 #3
significa que el elemento consistirá en cosas como
<subitem><space><subitem><space><subitem>
El segundo argumento obligatorio es el código que utiliza los argumentos configurados en la plantilla. Hay abreviaturas single
(predeterminadas si no se especifica nada) double
y triple
significan
format=#1
format=#1/#2
format=#1/#2/#3
respectivamente.