6

using the xfp package to calculate mathematical terms works fantastic and I am trying to automate some things to also make the \fp_eval:n input also presented properly with LaTeX notation.

My attempt is to therefore use l3regex commands to match the necessary expressions.

Now with the following command \regex_replace_all:nnN { (\() (.*) (\)/\() (.*) (\)) } { \c{frac} \cB\{ \2\cE\} \cB\{ \4\cE\} } \l_juergen_func_tl the first example works like a charm, however fails with the second example.

Code:

\documentclass{article}
\usepackage{xfp}

\begin{document}

\ExplSyntaxOn

\NewDocumentCommand{\showfuncandvalue}{ m m }
{% #1 = value, #2 = function
  \tl_set:Nn                                        \l_juergen_func_tl  { #2 }
    \regex_replace_all:nnN { \* } { \c{cdot} }      \l_juergen_func_tl
    \regex_replace_all:nnN { (\() (.*) (\)/\() (.*) (\)) } { \c{frac} \cB\{ \2\cE\} \cB\{ \4\cE\} }  \l_juergen_func_tl

    f(x) = \l_juergen_func_tl
    ;\quad
    f(#1) = \juergen_showfunc_value:nn { #1 } { #2 }
}

\cs_new_protected:Nn \juergen_showfunc_value:nn
{
  \tl_set:Nn                               \l_juergen_func_tl { #2 }
  \regex_replace_all:nnN { x } { (x) }     \l_juergen_func_tl
  \regex_replace_all:nnN { x } { \cP\#1 }  \l_juergen_func_tl
  \cs_set:NV \__juergen_showfunc_f:n       \l_juergen_func_tl
  \fp_eval:n
   {
    round ( \__juergen_showfunc_f:n { ( \fp_eval:n { ( #1 ) } ) } , 4 )
   }
}
\cs_generate_variant:Nn \cs_set:Nn { NV }

\ExplSyntaxOff

$$ \showfuncandvalue{2}{x-(4*x+3x^2)/(3-(2*x+x^2)) + 16} $$
$$ \showfuncandvalue{2}{(x+1)-(4*x+3x^2)/(3-(2*x+x^2)) - (16-x)} $$

The correct notation for the second example should be:
$$(x+1)-\frac{4\cdot x+3x^2}{3-(2\cdot x+x^2)}-(16-x)$$
\end{document}

enter image description here

Now my question is how to make the regEx pattern more intelligent to also make the second example work well. Note: There might be more pairs of parentheses in front of the fraction and of course after the fraction, which I didn't setup within the example.

My regEx pattern doesn't find the correct matching parentheses, that's obvious.

Any help to structure a regEx pattern that could translate xfp syntax into LaTeX syntax would be appreciated.

7

Imho it would be easier the other way round and start with the LaTeX-syntax: it is not easy to balance parantheses or braces in regular expressions, but TeX can balance braces without problems and \fpeval can handle macros and braces in its argument:

\documentclass{scrbook}
\usepackage{xfp}
\ExplSyntaxOn
\newcommand\fpdiv[2]{(#1)/(#2)}
\NewDocumentCommand{\showfuncandvalue}{ m m }
{% #1 = value, #2 = function
  \tl_set:Nn                                        \l_juergen_func_tl  { #2 }
    \regex_replace_all:nnN { ([0-9])x } { \1\*#1 }  \l_juergen_func_tl
    %\tl_show:N\l_juergen_func_tl
    \regex_replace_all:nnN { x } { #1 }             \l_juergen_func_tl
    %\tl_show:N\l_juergen_func_tl
    \regex_replace_all:nnN { \c{cdot} } { \* }      \l_juergen_func_tl
    %\tl_show:N\l_juergen_func_tl
    \regex_replace_all:nnN { \c{frac} } { \c{fpdiv} }\l_juergen_func_tl
    %\tl_show:N\l_juergen_func_tl

  f(x) = #2
  ;\quad
  f(#1) = \fp_eval:n{\l_juergen_func_tl}
}

\ExplSyntaxOff
\begin{document}

\[\showfuncandvalue{2}{x-\frac{4x+3x^2}{3-(2x+x^2)} + 16}\]

\[\showfuncandvalue{2}{(x+1)-\frac{4\cdot x+3x^2}{3-(2\cdot x+x^2)}-(16-x)}\]

\[\showfuncandvalue{2}{(x+1)-\frac{4\cdot x+3x^2}{3-(2\cdot x+x^{(1+1)})}-(16-x)}\]


\end{document}

enter image description here

3

Here I've opted for changing every left parenthesis to \juergenparen(, then let xparse's delimiter-matching code do its job (with an expandable command to allow further code to peek-ahead at the next token). Once every parenthesized group is replaced by \__juergen_paren:n { ... }, that function looks for / and \__juergen_paren:n { ... } following it and wraps that in \frac if relevant.

\documentclass{article}
\usepackage{xfp}

\begin{document}

\ExplSyntaxOn

\tl_new:N \l__juergen_func_tl
\fp_new:N \l__juergen_func_fp
\NewDocumentCommand{\showfuncandvalue}{ m m }
  { % #1 = value, #2 = function
    \juergen_showfunc_math:n {#2} ; \quad
    \juergen_showfunc_value:nn {#1} {#2}
  }
\cs_new_protected:Nn \juergen_showfunc_math:n
  {
    \juergen_showfunc_math_get:nN {#1} \l__juergen_func_tl
    f(x) = \l__juergen_func_tl
  }
\cs_new_protected:Nn \juergen_showfunc_math_get:nN
  {
    \tl_set:Nn #2 {#1}
    \tl_replace_all:Nnn #2 { * } { \exp_not:N \cdot } % Later we do x-expansion.
    % Wrap single-term numerator/denominator (such as "2*x^2") in parentheses.
    \regex_replace_all:nnN { ( [\w\^\*]+ ) \s* \/ } { \( \1 \) \/ } #2
    \regex_replace_all:nnN { \/ \s* ( [\w\^]+ ) } { \/ \( \1 \) } #2
    % Insert and expand "\juergenparen", which balances parentheses.
    \tl_replace_all:Nnn #2 { ( } { \juergenparen ( }
    \tl_set:Nx #2 {#2}
  }
\NewExpandableDocumentCommand {\juergenparen} {r()} { \__juergen_paren:n {#1} }
\cs_new_protected:Nn \__juergen_paren:n
  {
    \peek_charcode_remove_ignore_spaces:NTF /
      { \__juergen_paren_div:n {#1} }
      { (#1) }
  }
\cs_new_protected:Nn \__juergen_paren_div:n
  {
    \peek_meaning_remove_ignore_spaces:NTF \__juergen_paren:n
      { \__juergen_paren_div:nn {#1} }
      { (#1) / \msg_error:nnn { juergen } { no-denominator } {#1} }
  }
\cs_new_protected:Nn \__juergen_paren_div:nn { \frac{#1}{#2} }
\msg_new:nnn { juergen } { no-denominator }
  { No~denominator~after~"(#1)/". }
\cs_new_protected:Nn \juergen_showfunc_value:nn
  {
    \fp_set:Nn \l__juergen_func_fp {#1}
    \tl_set:Nn \l__juergen_func_tl {#2}
    \tl_replace_all:Nnn \l__juergen_func_tl { x } { \l__juergen_func_fp }
    f ( \fp_use:N \l__juergen_func_fp )
    = \fp_eval:n { round ( \l__juergen_func_tl , 4 ) }
  }

\ExplSyntaxOff

\[ \showfuncandvalue{2}{x-(4*x+3x^2)/(3-(2*x+x^2)) + 16} \]
\[ \showfuncandvalue{2}{(x+1)-(4*x+3x^2)/(3-(2*x+x^2)) - (16-x)} \]

The correct notation for the second example should be:
\[(x+1)-\frac{4\cdot x+3x^2}{3-(2\cdot x+x^2)}-(16-x)\]
\end{document}

For the "value" part, rather than wrapping the value in parentheses and re-computing it several times it may be better to just save it in a floating point variable as I did here, then to replace all x by that. Note that that would fail badly for negative x (say, -1) if one used token list variables: x^2 would be turned to \l_my_tl ^2, which would expand to -1^2, parsed as -(1^2). In contrast, floating point variables have extra information that tells l3fp (underlying xfp) to treat them as a single object, giving (-1)^2.

  • Glad to see that the matching parentheses work as well, whenever I prefer the "reverse approach" of Ulrike -- this seems to be easier to realize ... – user151328 Dec 30 '17 at 22:27
2

thanks to Ulrike Fischer's answer with her "reverse attempt" (which makes things much easier concerning RegEx), I modified her code a little, so that #1 also may be input with LaTeX-syntax and now also allows a comma as decimal delimiter.

This made my day:

\documentclass{article}
\usepackage{siunitx,xfp}
\sisetup{output-decimal-marker = {,}}
\ExplSyntaxOn
\newcommand\fpdiv[2]{(#1)/(#2)}
\newcommand\fpsqrt[1]{sqrt(#1)}
\NewDocumentCommand{\showfuncandvalue}{ m m }
{% #1 = value, #2 = function
  \tl_set:Nn                                           \l_juergen_value_tl { #1 }
    \regex_replace_all:nnN { \c{ln} } { ln }           \l_juergen_value_tl
    \regex_replace_all:nnN { \c{frac} } { \c{fpdiv} }  \l_juergen_value_tl
    \regex_replace_all:nnN { \c{sqrt} } { \c{fpsqrt} } \l_juergen_value_tl
    \regex_replace_all:nnN { \c{cdot} } { \* }         \l_juergen_value_tl
    \regex_replace_all:nnN { , } { \. }                \l_juergen_value_tl
%---------------------------------------------------------------------------------
  \tl_set:Nn                                           \l_juergen_func_tl  { #2 }
    \regex_replace_all:nnN { ([0-9])x } { \1\*(\c{l_juergen_value_tl}) }   \l_juergen_func_tl
    \regex_replace_all:nnN { x }        { (\c{l_juergen_value_tl}) }       \l_juergen_func_tl
    \regex_replace_all:nnN { \c{cdot} } { \* }         \l_juergen_func_tl
    \regex_replace_all:nnN { \c{ln} } { ln }           \l_juergen_func_tl
    \regex_replace_all:nnN { \c{frac} } { \c{fpdiv} }  \l_juergen_func_tl
    \regex_replace_all:nnN { \c{sqrt} } { \c{fpsqrt} } \l_juergen_func_tl
    \regex_replace_all:nnN { , } { \. }                \l_juergen_func_tl
  f(x) = #2
  ;\quad
  f(#1) = \num { \fp_eval:n { \l_juergen_func_tl} }
}
\ExplSyntaxOff

\begin{document}
\[\showfuncandvalue{-2\cdot\sqrt{2}}{x-\frac{4x+3x^2}{3-(2x+x^2)} + 16}\]

\[\showfuncandvalue{\ln(2)}{(x+1)-\frac{4\cdot x+3x^2}{3-(2\cdot x+x^2)}-(16-x)}\]

\[\showfuncandvalue{2^{\ln(2)}}{-\frac{\frac{\sqrt{2,34}\cdot 3}{\sqrt{3}-1}}{\frac{2\cdot x}{3}}\cdot (x+1)-\frac{4\cdot x+3\cdot x^2}{3-(2\cdot x+x^{(1+1)})}-(16-x)}\]
\end{document}

Since I am a new member of this list, I couldn't yet give a comment. So here the new results -- GREAT!!!

enter image description here

Your Answer

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