Short answer: are you sure you gain in code readability?
Long answer: yes, it can be done.
\documentclass{article}
\usepackage{xparse,l3regex}
\ExplSyntaxOn
\NewDocumentCommand{\functiondef}{mr()m}
{% #1 = function name (a macro), #2 = variable, #3 = formula
\tl_set:Nn \l_tmpa_tl { #3 }
\regex_replace_all:nnN { \b#2\b } { \cP\#1 } \l_tmpa_tl
\cs_gset:cV { function_inner_\cs_to_str:N #1 :n } \l_tmpa_tl
\cs_new:Npn #1 ( ##1 )
{
\use:c { function_inner_\cs_to_str:N #1 :n } { ##1 }
}
}
\cs_generate_variant:Nn \cs_gset:Nn { cV }
\cs_set_eq:NN \eval \fp_eval:n
\ExplSyntaxOff
\functiondef\f(x){x^3 + 3*exp(x)}
\begin{document}
\eval{\f(42)}
\end{document}
Here I use \fp_eval:n, adapt it to PGF.

If I do
echo '42^3+3*e(42)' | bc -l
I get
5217824824561577230.18404391083370567842
The accuracy depends, of course, on the number crunching engine.
Explanation
The replacement text is stored in a sequence; every appearance of x (the stated variable name, more generally) surrounded by word boundaries is replaced by #1. This token list is then passed to \cs_gset:cV that builds a regular macro named \function_inner_f:n (the actual name is built from the first argument to \functiondef) and then the macro \f with the argument surrounded by parentheses is defined to use \function_inner_f:n.