4

I'm still not too used to \pgfmath... commands and calc tikzlibrary operations.

Today I wanted to use an operation between two macros defined with \pgfmathsetlengthmacro as a upper foreach limit. Something like

\pgfmathsetlengthmacro\X{10mm}
\pgfmathsetlengthmacro\Y{2mm}

\foreach \i in {0,...,\X/\Y} 

or

\foreach \i in {0,...,{int(\X/\Y)}} 

or

\foreach \i in {0,...,{int(scalar(\X/\Y))}} 

Second and third solutions were written because the previous didn't work, but without really understanding where is the problem.

None of this three examples worked and finally I could solve it with an auxiliary macro

\pgfmathsetmacro{\XdivY}{\X/\Y}
\foreach \i in {0,...,\XdivY}

Could you explain me why my first tests didn't work? How can I write a correct operation between \X and \Y as a limit for \foreach?

In case you want some code for testing, here it is:

\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{calc}

\begin{document}
\begin{tikzpicture}
\pgfmathsetlengthmacro\X{10mm}
\pgfmathsetlengthmacro\Y{2mm}
\pgfmathsetmacro{\XdivY}{\X/\Y}

\foreach \i in {0,...,\XdivY}
    \node at (\i,0) {\i};

% This doesn't work 
%\foreach \i in {0,...,{int(scalar(\X/\Y))}}
%   \node at (\i,0) {\i};

\end{tikzpicture}
\end{document}
Ignasi
  • 136,588

2 Answers2

2

With some new macros of mine; some part of the code is not used.

\documentclass{article}
\usepackage[T1]{fontenc}
\usepackage{xparse,tikz}

\setlength{\parindent}{0pt} % just for the example

\ExplSyntaxOn
\providecommand\fpeval{\fp_eval:n}
\providecommand\dimtofp{\dim_to_fp: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
 {
  \manual_fp_step_inline:nnnn
   { \l__manual_nforeach_start_tl }
   { \l__manual_nforeach_step_tl }
   { \l__manual_nforeach_end_tl }
   { #1 }
 }

% a replacement for \fp_step_inline:nnnn
\seq_new:N \l__manual_fp_step_seq
\fp_new:N \l__manual_fp_step_start_fp

\cs_new_protected:Nn \manual_fp_step_inline:nnnn
 {
  \int_gincr:N \g__manual_fp_map_int
  \seq_clear_new:c { l__manual_fp_step_ \int_use:N \g__manual_fp_map_int _seq }
  \fp_compare:nTF { #2 < \c_zero_fp }
   {
    \__manual_fp_step_make_neg:nnn { #1 } { #2 } { #3 }
   }
   {
    \__manual_fp_step_make_pos:nnn { #1 } { #2 } { #3 }
   }
  \seq_map_inline:cn { l__manual_fp_step_ \int_use:N \g__manual_fp_map_int _seq } { #4 }
  \int_gdecr:N \g__manual_fp_map_int
 }
\cs_new_protected:Nn \__manual_fp_step_make_neg:nnn
 {
  \fp_set:Nn \l__manual_fp_step_start_fp { #1 }
  \fp_do_while:nn { \l__manual_fp_step_start_fp >= #3 }
   {
    \seq_put_right:cx { l__manual_fp_step_ \int_use:N \g__manual_fp_map_int _seq }
     { \fp_eval:n { \l__manual_fp_step_start_fp } }
    \fp_add:Nn \l__manual_fp_step_start_fp { #2 }
   }
 }
\cs_new_protected:Nn \__manual_fp_step_make_pos:nnn
 {
  \fp_set:Nn \l__manual_fp_step_start_fp { #1 }
  \fp_do_while:nn { \l__manual_fp_step_start_fp <= #3 }
   {
    \seq_put_right:cx { l__manual_fp_step_ \int_use:N \g__manual_fp_map_int _seq }
     { \fp_eval:n { \l__manual_fp_step_start_fp } }
    \fp_add:Nn \l__manual_fp_step_start_fp { #2 }
   }
 }

\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

\newcommand{\ignasiX}{30mm}
\newcommand{\ignasiY}{6mm}
\newcommand{\ignasiA}{45pt}
\newcommand{\ignasiB}{17pt}

\begin{document}

\begin{tikzpicture}
\nforeach{
  start=0,
  end=\fpeval{trunc(\dimtofp{\ignasiX}/\dimtofp{\ignasiY})},
  step=1,
}{
  \node at (#1,0) {#1};
}
\end{tikzpicture}

\begin{tikzpicture}
\nforeach{
  type=fp,
  start=0,
  end=\fpeval{trunc(\dimtofp{\ignasiA}/\dimtofp{\ignasiB})},
  step=0.3,
}{
  \node at (#1,#1) {#1};
}
\end{tikzpicture}

\end{document}

enter image description here

Of course you can do it also with \foreach:

\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{calc}

\begin{document}

\begin{tikzpicture}
\pgfmathsetlengthmacro\X{10mm}
\pgfmathsetlengthmacro\Y{2mm}

\foreach \i in {0,...,\numexpr\dimexpr\X/\dimexpr\Y\relax}
    \node at (\i,0) {\i};

\end{tikzpicture}

\end{document}

enter image description here

egreg
  • 1,121,712
  • You should buy an abacus for the sake of convenience :-). – cfr Sep 15 '16 at 00:30
  • I won't be able to remember all these macros every time I want to use a foreach, so I'll try to remember your second solution and percusse's comment. :-) – Ignasi Sep 15 '16 at 18:31
  • @Ignasi Maybe they'll become a package in the future; they'll be the topic of my talk at the next GuITmeeting. – egreg Sep 15 '16 at 19:37
1

As percusse said, the \foreach macro doesn't evaluate the elements of a list – at least not by default.

However, the /pgf/foreach/parse key activates the parsing for the last element in the list. For some reason its default value is false and not true as with other boolean keys, so you'd need parse=true or a quick fix like

\pgfkeys{/pgf/foreach/parse/.default=true}

It should be noted that you can't use more complex elements (but they're rarely are with ...) and the last element needs to be parseable by \pgfmathparse, of course.

Though, for simple calculations \numexpr might still be the best tool since it's probably faster than using the full PGFMath parser.

For more complex lists, I'd suggest taking a look at use int and use float from another answer of mine.

Code

\documentclass[tikz]{standalone}
% for some reason, parse doesn't
% default to parse=true, fix it:
\pgfkeys{/pgf/foreach/parse/.default=true}
\begin{document}
\begin{tikzpicture}
\pgfmathsetlengthmacro\X{10mm}
\pgfmathsetlengthmacro\Y{2mm}
\pgfmathsetmacro{\XdivY}{\X/\Y}

\foreach \i in {0,...,\XdivY} \node at (\i,0) {\i};

\foreach \i[parse] in {0,...,\X/\Y} \node at (\i,-1) {\i}; \end{tikzpicture} \end{document}

Code

Qrrbrbirlbel
  • 119,821