You could do it like this, but it is almost unreadable. I hope the explanations are helpful:
def multiadder(n):
assert n > 0
if n == 0:
return 0
else:
return (lambda f: lambda i, n, sm: f(f, i, n, sm))(
lambda rec, i, n, sm: sm+i if n == 0 else lambda j: rec(rec, j, n-1, sm+i)
)(0, n, 0)
See it run on repl.it.
How it works
The return value consists of three major parts:
(lambda f: lambda i, n, sm: f(f, i, n, sm))
In short, this function assigns a name to a function, so it can be called recursively. In more detail:
It takes a function f that must itself accept 4 arguments, of which the first should be a self-reference.
The function that is returned here takes the three other arguments, and returns the recursive call of f.
Part two is the real core:
(lambda rec, i, n, sm: sm+i if n == 0 else lambda j: rec(rec, j, n-1, sm+i))
This is passed as argument to the first function above, making recursion possible.
This function takes the 4 arguments mentioned above, and applies the specific logic:
i is the number that must be added
n is the number of values still expected after this one
sm is the so far accumulated sum (excluding i)
Depending on whether more values are expected this function returns the final result (sm+i) or
a function with one argument that will do the same as described here (recursion) with decreased n, and adapted sum.
Finally, the initial values are passed to the above:
(0, n, 0)
Meaning, we start with number 0 (dummy), n expected values, and a current sum of 0.
Note
As the recursion in the above code does not involve a call to multiladder, and the assertion really excludes the if condition to be true, we can do without that if...else:
def multiadder(n):
assert n > 0
return (lambda f: lambda i, n, sm: f(f, i, n, sm))(
lambda rec, i, n, sm: sm+i if n == 0 else lambda j: rec(rec, j, n-1, sm+i)
)(0, n, 0)