TL/DR: Why do the Sage functions defined below behave differently?
Consider the following recursively-defined sequence of functions, $g_n: \mathbb{R}\to\mathbb{R}$: $$ g_0(x) = 1, $$ and for $n\geq 1$, $$ g_n(x) = \int_0^x g_{n-1}(x-t) \, dt. $$ We can prove by induction that $g_n(x) = \frac{1}{n!}x^n$, for all $n$ and $x$. For the induction step, treat $x$ as fixed and substitute $u = x-t$, $du= -dt$. Alternatively, we could use differentiation under the integral sign to show that $g_n'(x) = \frac{1}{(n-1)!}x^{n-1}$ for $n\geq 1$, and then note that $g_n(0) = 0$. There may be other approaches.
So, the result is not in doubt. My question is about how one might check this result in Sage.
Here is an implementation that seems to work:
t = var('t')
x = var('x')
def g(n):
if n == 0:
return lambda x: 1
else:
expression = integral(g(n-1)(x-t), t, 0, x)
return lambda x: expression.subs(x=x)
For example if I do g(8)(1), I get 1/40320. This is the correct result, because $8!=40320$. (Here I am implementing $g_8$ as g(8), so technically g is a function which takes natural numbers to functions--in other words, g is a sequence of functions.)
Here's one that doesn't work (with t,x defined as above):
def h(n):
if n == 0:
return lambda x: 1
else:
expression = integral(h(n-1)(x-t), t, 0, x)
return lambda x: expression
Now h(8)(1) gives x^8, which clearly is not what I want, because $g_8(1)$ should not have any free variables! But the only difference between these two versions is the .subs(x=x) in the last line, so I guess my question is:
How does "substituting $x$ for $x$" cause different behavior?
I suspect that the issue is some kind of subtlety about reusing the same variable name at different levels of recursion, but I still would have (naïvely) expected the two versions to either both work or both fail.
As you have noticed, there is a difference between the functions
and
where
expressionis a symbolic expression which is defined in terms of some variablevar('x').Namely, e.g.
a(5)evaluates toexpression.subs(x=5), whereasb(5)evaluates toexpressionwhich will still depend onvar('x')because no substitution is done. Notebdoes not use its argument at all, whereasapasses its argument as a keyword argument tosubswhere the key has the name'x'(corresponding to the name of the variable which you want to substitute). Your confusion is probably due to the names of thelambdas arguments. The above are equivalent to:and
which makes it clearer what happens.