4

The below code is wrong but it illustrates what I am trying to achieve. How would you write the g command?

\usepackage{xfp}
\newcommand{\f}[1]{
    \inteval{(#1 - \a) / (\b - \a)}
}
\newcommand{\g}[1]{
    f=\f{#1}
    if (f>h1)
       f = f-(h1-l1)
    if (f>h2)
       f = f-(h2-l2)
    f % meaning return f. My purpose is to return an integer not to type anything.
}

Ideally, I would have a list of couples (l_i,h_i) where h_i>l_i and I would remove to f all (h_i-l_i) whose h_i < f.

If I have few of them I am ok to write them manually. Otherwise I would appreciate iterating over a list or other structure.

Please suggest the lightest possible syntax. That's why I appreciate xfp. Apart from \inteval and \fpeval the rest is math as I would write it. For Latex newbies too much added stuff is discouraging.

4
  • 1
    It's better if you show an example of what you're trying to achieve with some more detail. – egreg May 4 at 21:35
  • 1
    Is switching to LuaLaTeX an option for you? – Mico May 4 at 21:42
  • 1
    @Mico I'll give it a try since it seems to simplify the syntax of all the computations we might want to do. I need to check if it fits in my stack. I am today using a vscode extension called LaTeX workshop that helps me build remotely on a docker container my latex file (that is present locally thanks to docker volumes). I need to check that the LaTex workshop extension works with LuaLaTex and that I can do the build as easily. Thank you for the suggestion. – Filippo Guenzi May 5 at 18:25
  • 1
    @Mico Thank you again for your advice. After having tried desperately to have my functions work in LateX dealing with all that new syntax and strange concepts it took me seconds to make it work with LuaLateX. I am really grateful. I can concentrate on my logic and forget all the LaTeX complexity. And luckily it is compatible with my stack. You saved me weeks of hard work. – Filippo Guenzi May 6 at 21:04
4

TeX has no concept of “return value” like other programming languages do, so things are usually much trickier to program (but far from impossible, of course).

TeX works roughly with expansion and assignments, and you can use either here. Here's one version which works by expansion (which means you can use \g as argument to other functions or integer expressions):

\newcommand{\f}[1]{%
  \inteval{(#1 - \a) / (\b - \a)}}

\newcommand{\g}[1]{%
  \expandafter\gaux\expandafter{\number\f{#1}}}
\newcommand{\gaux}[1]{%
  \ifnum#1>\hI
    \inteval{#1-(\hI-\lI)}%
  \else\ifnum #1>\hII
    \inteval{#1-(\hII-\lII)}%
  \fi\fi}

Here the command \g forces the expansion of \f{#1} using \number, and passes the result to \gaux. \gaux then does the conditionals using \ifnum and returns the expressions in \inteval. I used \hI instead of h1 because you can't (usually) have numbers in command names, and the same for other variables.

And here's a possibility using assignments. This means that the result in returned in \freturn (you can write the result using \the\freturn), and you cannot nest \g inside \g itself or another similar command:

\newcommand{\f}[1]{%
  \inteval{(#1 - \a) / (\b - \a)}}

\newcount\freturn
\newcommand{\g}[1]{%
  \freturn=\f{#1}
  \ifnum\freturn>\hI
    \freturn=\inteval{\freturn-(\hI-\lI)}
  \else\ifnum \freturn>\hII
    \freturn=\inteval{\freturn-(\hII-\lII)}
  \fi\fi}

This first stores the output of \f{#1} in \freturn, then uses \freturn to compute the conditionals and then eventually store the output in \freturn again. Note that in this case \g itself does not write anything to the output, you have to write \g{<num>} \the\freturn.

5
  • Thank you very much for your reply. – Filippo Guenzi May 5 at 18:11
  • I had to write \ifnum#1>\inteval{\hI} to make it work (on every comparison). Maybe because I have defined it with \def\hI{100}. What I would have liked to do is not an if...elseif...fi, but is instead an if...fi if...fi if...fi ... changing in each if the same variable until it gets the proper value. That's why I was talking about removing a cumulative sum in the title. Is that possible? – Filippo Guenzi May 5 at 19:43
  • 1
    @FilippoGuenzi I won't have time to look in the near future, but try \g{8} \the\xTemp – Phelype Oleinik May 5 at 19:52
  • Nice ! it works, thank you :) – Filippo Guenzi May 5 at 19:58
  • 1
    @FilippoGuenzi That's because the second approach, which works with assignments to the variable \xTemp, does not print the result at the end. You have to add \the\xTemp to make it print the value (you can add it to the macro \g if you need) – Phelype Oleinik May 5 at 20:00
1

Thank you for your help.

I integrated your suggestions to have this working example :

\documentclass{article}
\def\maxX{100}
\def\fd{0}
\def\ld{100}
\def\lI{0}
\def\hI{10}
\def\lII{20}
\def\hII{30}
\usepackage{xfp}
\newcommand{\f}[1]{%
    \inteval{\maxX * (#1 - \fd) / (\ld - \fd)}
}
\newcount\xTemp
\newcommand{\g}[1]{%
  \expandafter\gaux\expandafter{\number\f{#1}}}
\newcommand{\gaux}[1]{%
  \xTemp=#1
  \ifnum#1>\inteval{\hI}
    \xTemp=\inteval{\xTemp-(\hI-\lI)}%
  \fi
  \ifnum #1>\inteval{\hII}
    \xTemp=\inteval{\xTemp-(\hII-\lII)}%
  \fi
}
\begin{document}
% below is just a way for me to see the results
\begin{tabular}{ r r }
    $x$ & $g(x)$ \\
    \hline
    0 & \g{0} \the\xTemp\\
    15 & \g{15} \the\xTemp\\
    35 & \g{35} \the\xTemp\\
    \hline
\end{tabular}
\end{document}

I will now look for a way to iterate over those (l_i, h_i) to repeat the same calculation several times without having to write them all.

Thank you again.

0

If I understand correctly your aim, you have a list of subintervals and you want to do the shown calculation using all subintervals.

\documentclass{article}

\ExplSyntaxOn

\int_const:Nn \c_filippo_a_int { 0 }   % fd
\int_const:Nn \c_filippo_b_int { 100 } % ld
\int_const:Nn \c_filippo_m_int { 100 } % maxX

\int_new:N \l__filippo_step_int

\seq_const_from_clist:Nn \c_filippo_l_seq {  0,20,40,60,80 } % l_i
\seq_const_from_clist:Nn \c_filippo_h_seq { 10,30,50,70,90 } % h_i

\cs_new:Nn \filippo_f:n
 {
  \int_eval:n
   {
    100 * ( #1 - \c_filippo_a_int ) / ( \c_filippo_b_int - \c_filippo_a_int )
   }
 }
\cs_generate_variant:Nn \filippo_f:n { e }

\cs_new:Nn \filippo_g:n
 {
  \__filippo_g:ee { \filippo_f:n { #1 } } { 0 }
 }

\cs_new:Nn \__filippo_g:nn
 {
  \int_compare:nTF { #2 < \seq_count:N \c_filippo_l_seq }
   {% do the comparison
    \int_compare:nTF { #1 > \seq_item:Nn \c_filippo_h_seq { #2 + 1 } }
     {
      \__filippo_g:ee
       {
        \int_eval:n
         {
          #1 -
          \seq_item:Nn \c_filippo_h_seq { #2 + 1 } +
          \seq_item:Nn \c_filippo_l_seq { #2 + 1 }
         }
       }
       { \int_eval:n { #2 + 1 } }
     }
     {
      \__filippo_g:ee { #1 } { \int_eval:n { #2 + 1 } }
     }
   }
   {% we've ended the recursion, return the value
    #1
   }
 }
\cs_generate_variant:Nn \__filippo_g:nn { ee }

\NewExpandableDocumentCommand{\g}{m}
 {
  \filippo_g:n { #1 }
 }

\ExplSyntaxOff

\begin{document}

\begin{tabular}{ r r }
    $x$ & $g(x)$ \\
    \hline
     0 & \g{0} \\
     1 & \g{1} \\
    10 & \g{10} \\
    12 & \g{12} \\
    15 & \g{15} \\
    35 & \g{35} \\
    \hline
\end{tabular}

\bigskip

\edef\test{\g{35}}
\texttt{\meaning\test}

\end{document}

The command \g is fully expandable, as shown in the last line.

The second argument to \__filippo_g:ee is the loop index for the recursion. The function calls itself on a new input until the list of subintervals is finished and at that point, the first argument is returned, otherwise the loop index is incremented.

enter image description here

1
  • Thank you for your reply. I am a newbie in LaTex and I still miss basic concepts like expandability. I have read a nice post yesterday about expandability that gives me hope. I am far from being able to understand all those new words and concepts, as I haven't yet fully understood the previous answers, but I don't despair. Thank you to introduce me to the for loop construct in latex and probably to many more things whose reach I don't yet understand. – Filippo Guenzi May 6 at 7:15

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.