4

I want use array along with fp package.

\FPadd\xx{\somevalues(1)}{\somevalues(3)} works well

\FPeval\xx{\somevalues(1)+\somevalues(1)} NOT working

How to use \FPeval in array?

\documentclass{article}
\usepackage{fp}
\usepackage{siunitx,amsmath}
%\usepackage{xparse}
\ExplSyntaxOn
\NewDocumentCommand{\newarray}{m}
 {
  \seq_new:c { l_hafid_array_ \cs_to_str:N #1 _seq }
  \cs_new:Npn #1 (##1)
   {
    \seq_item:cn { l_hafid_array_ \cs_to_str:N #1 _seq } { ##1 }
   }
 }
\NewDocumentCommand{\readarray}{mm}
 {
  \seq_set_split:cnn { l_hafid_array_#1_seq } { & } { #2 }
 }
\cs_generate_variant:Nn \seq_set_split:Nnn { c }
\ExplSyntaxOff

\begin{document}

\newarray\somevalues
\readarray{somevalues}{1&2&3.5&4&5}

\begin{align*}
v_1 &= \somevalues(1)\\
v_2 &= \somevalues(2)\\
v_3 &= \somevalues(3)\\
v_4 &= \somevalues(4)\\
v_5 &= \somevalues(5)
\end{align*}

First number = \somevalues(1)\\
Second number = \somevalues(2)

\FPadd\xx{\somevalues(1)}{\somevalues(2)}
\FPeval{\xx}{round(xx,4)}%
sum of first two numbers = \xx

\FPeval\xx{\somevalues(1)+\somevalues(2)}
\FPeval{\xx}{round(xx,4)}%
sum of first two numbers = \xx


%
\newcount\count
\count=5
\FPset\ans{0}
\loop
%\somevalues(\count)\\
\FPadd\ans{\ans}{\somevalues(\count)}
%\FPeval\ans{\ans+\somevalues(\count)}
\advance \count by -1
\unless\ifnum \count<1
\repeat
%
\FPeval{\ans}{round(ans,4)}%
sum of all numbers = \ans

\end{document}
sandu
  • 7,950
  • Since you're using expl3 anyway why don't use use l3fp instead of fp? – Phelype Oleinik Mar 21 '19 at 10:58
  • 1
    @Phelype Oleinik Not familiar with expl3. I copied expl3 code, to define array. – sandu Mar 21 '19 at 11:01
  • The answer I posted deals only with making your syntax work as you'd expect. The array handling code could be changed to use l3fp's tuples, which are a built-in way of handling arrays, so they have a good support in l3fp. You can take a look at the LaTeX3's interfaces to see what programming facilities it offers. – Phelype Oleinik Mar 21 '19 at 11:42

2 Answers2

4

Your code does not work because the way that the fp package parses its input. In TeX when you define a delimited macro the delimiter text must always be present, so \somevalues must always be followed by a (, then some text, then a closing ). When fp tries to parse your expression it separates the tokens and adds other tokens in between, then the error you get is:

! Use of \somevalues doesn't match its definition.
<argument> \somevalues  
                        \FP@gen@code {2}

because what follows \somevalues is \FP@gen@code, not a (.

Since you're using expl3 anyway you can use its l3fp module, which offers an expandable FP engine. Being expandable means that you can do

The~value~of~$\pi^2$~is:~\fp_eval:n { pi^2 }

or even

\setlength{\textwidth}{\fp_eval:n{ 10*30 } pt}

Here I emulated fp's macros \FPadd, \FPset and \FPeval using l3fp instead. Note that these emulated macros aren't expandable anymore.

Now you can do, for instance:

\FPset\ans{5}
\FPeval\ans{ans+10}
ans is: \ans % prints 15

Every macro, say, \ans, used as the first argument to \FP<something> is defined with the result of the operation, plus a FP word called ans is defined and will contain that same value, so inside an expression you can use either \ans or ans.

CAUTION! This does not perform any verification whatsoever if the involved macros are already defined, so you can accidentally overwrite something important. \FPeval\pi{6.283185} might not be a good idea.

Here's the code (I only changed the definitions of the FP functions; your array handling and test code is the same):

\documentclass{article}
% \usepackage{fp}
\usepackage{siunitx,amsmath}
%\usepackage{xparse}
\ExplSyntaxOn
\NewDocumentCommand{\newarray}{m}
 {
  \seq_new:c { l_hafid_array_ \cs_to_str:N #1 _seq }
  \cs_new:Npn #1 (##1)
   {
    \seq_item:cn { l_hafid_array_ \cs_to_str:N #1 _seq } { ##1 }
   }
 }
\NewDocumentCommand{\readarray}{mm}
 {
  \seq_set_split:cnn { l_hafid_array_#1_seq } { & } { #2 }
 }
\cs_generate_variant:Nn \seq_set_split:Nnn { c }


% Stolen from l3fp:
\fp_new:N \l__sandu_temp_fp
\cs_set_protected:Npn \sandu_fp_name:nn #1 #2
  {
    \cs_undefine:c { c__sandu_#1_fp }
    \fp_const:cn { c__sandu_#1_fp } { #2 }
    \__sandu_define_fp_name:cn { c__sandu_#1_fp } { #1 }
  }
\cs_new_protected:Npn \__sandu_define_fp_name:Nn #1 #2
  {
    \cs_set:cpn { __fp_parse_word_#2:N }
      { \exp_after:wN #1 \exp:w \exp_end_continue_f:w \__fp_parse_infix:NN }
  }
\cs_generate_variant:Nn \__sandu_define_fp_name:Nn { c }
\cs_generate_variant:Nn \sandu_fp_name:nn { f }


% Emulating fp.sty
\NewDocumentCommand \FPadd {m mm}
  {
    \tl_set:Nx #1 { \fp_eval:n { #2 + #3 } }
    \sandu_fp_name:fn { \cs_to_str:N #1 } { #1 }
  }
\NewDocumentCommand \FPeval {m m}
  {
    \tl_set:Nx #1 { \fp_eval:n { #2 } }
    \sandu_fp_name:fn { \cs_to_str:N #1 } { #1 }
  }
\NewDocumentCommand \FPset {m m}
  {
    \tl_set:Nx #1 { \fp_eval:n { #2 } }
    \sandu_fp_name:fn { \cs_to_str:N #1 } { #1 }
  }
\ExplSyntaxOff

\begin{document}

\newarray\somevalues
\readarray{somevalues}{1&2&3.5&4&5}

\begin{align*}
v_1 &= \somevalues(1)\\
v_2 &= \somevalues(2)\\
v_3 &= \somevalues(3)\\
v_4 &= \somevalues(4)\\
v_5 &= \somevalues(5)
\end{align*}

First number = \somevalues(1)\\
Second number = \somevalues(2)

\FPadd\xx{\somevalues(1)}{\somevalues(2)}
\FPeval{\xx}{round(xx,4)}%
sum of first two numbers = \xx

\FPeval\xx{\somevalues(1)+\somevalues(2)}
\FPeval{\xx}{round(xx,4)}%
sum of first two numbers = \xx


%
\newcount\counter
\counter=5
\FPset\ans{0}
\loop
%\somevalues(\counter)\\
\FPadd\ans{\ans}{\somevalues(\counter)}
%\FPeval\ans{\ans+\somevalues(\counter)}
\advance \counter by -1
\unless\ifnum \counter<1
\repeat
%
\FPeval{\ans}{round(ans,4)}%
sum of all numbers = \ans

\end{document}

which prints:

enter image description here


P.S.: Don't do \newcount\count because you'll redefine the TeX primitive \count. Use another name.

  • The code does not work for beamer class – sandu Mar 21 '19 at 12:28
  • @sandu It's because you are doing \newcount\count. \count is the TeX primitive which allows you to access a counter register. Once you redefine it you're in trouble (it worked for article because it doesn't try to define any counter at \end{document}; beamer does and chaos ensues). Change the name to, say, \counter, and it will work :) – Phelype Oleinik Mar 21 '19 at 12:43
2

Don't use \newcount if you don't know about its quirks: for instance, it doesn't check whether the control sequence is already defined. If you want another spectacular wreckage, try \newcount\box.

You're mixing syntaxes. Something like

\newarray\somevalues
\setarray\somevalues{1&2&3}

\somevalues(1)

is in arrayjobx style and is not something I'd use, because it can confuse the parser for \fpeval. Better standard argument delimiters, that is, braces.

There is no need to use fp when you're already using expl3.

\documentclass{article}
\usepackage{siunitx,amsmath}
\usepackage{xparse,xfp}

\ExplSyntaxOn
\NewDocumentCommand{\newarray}{m}
 {
  \seq_new:c { l_hafid_array_#1_seq }
  \cs_new:cpn { #1 } ##1
   {
    \seq_item:cn { l_hafid_array_#1_seq } { ##1 }
   }
 }
\NewDocumentCommand{\readarray}{mm}
 {
  \seq_set_split:cnn { l_hafid_array_#1_seq } { & } { #2 }
 }
\cs_generate_variant:Nn \seq_set_split:Nnn { c }

\NewExpandableDocumentCommand{\sumarray}{O{15}m}
 {
  \fp_eval:n { round( \seq_use:cn { l_hafid_array_#2_seq } { + }, #1 ) }
 }

\ExplSyntaxOff

\begin{document}

\newarray{somevalues}
\readarray{somevalues}{1&2&3.5&4&5}

\begin{align*}
v_1 &= \somevalues{1}\\
v_2 &= \somevalues{2}\\
v_3 &= \somevalues{3}\\
v_4 &= \somevalues{4}\\
v_5 &= \somevalues{5}
\end{align*}

First number = \somevalues{1}

Second number = \somevalues{2}

sum of first two numbers = 
\fpeval{ round(\somevalues{1}+\somevalues{2},4) }

Sum of the array \sumarray[4]{somevalues}

Sum of the array \sumarray[0]{somevalues}

\end{document}

The optional argument to \sumarray is the number of digits for the rounding.

enter image description here

egreg
  • 1,121,712