0

I'm messing with python, and I'm looking for a way to replicate a for loop in a lambda.

Basically, I'd like to convert the function below to a lambda doing the same thing :

def basicForLoop(x):
    for i in range(x):
        print(i)

basicForLoop(100)

For now, I've managed to do it by using recursion and increasing the value to each new recursion :

(lambda f: lambda x: f(f,0, x))(lambda f,current,max: print(current) or f(f, current+1, max) if current <= max else None)(100)

This work rather well, but it hit the max recursion depth as soon as the number start to be too big, so I'm looking for a way to rearrange this lambda so that it can be used without worrying about the recursion depth to make it truly equivalent to the original function.

EDIT : I'm looking for a way to do this while keeping the loop logic directly inside the lambda, delegating the loop to another function like map, join, ... isn't what I'm looking for.

PS. I know very well that this is an abomination that should never be used in reality but I'm just curious about it.

8
  • (lambda x: any(map(print, range(x))))(10) ? Commented Dec 8, 2022 at 15:02
  • Sorry for not adding this detail in the question, but I'd like to keep the for logic in the lambda without delegating it to some other function that will do the loop. Commented Dec 8, 2022 at 15:12
  • Make it generator expression: (lambda x: any(print(i) for i in range(x)))(10). I'm using any() as a memory-efficient way to consume generator. You can simply unpack it or turn into a list comprehension, but it will return list of N None elements. Commented Dec 8, 2022 at 15:15
  • I think your lambda as written is complex enough to deserve both formatting and more explanation than "read this". Commented Dec 8, 2022 at 15:17
  • Are you using or as a way to just sequence multiple statements within the body of a lambda? Commented Dec 8, 2022 at 15:20

2 Answers 2

1

I'm pretty sure this is impossible.

So I'm assuming you want to keep pretty much all of the logic handled by your lambdas, not even using a range. If that's the case, you're not going to get a stack-safe solution in Python. In other languages you could use something called "Tail Recursion", which allows the interpreter/compiler to collapse some recursive calls down to a single stack frame, but Python does not support that.

I don't think you can make this use fewer stack frames, either. Rewriting and re-formatting a bit, and adding explicit names and more print statements:

buildRecursive = (lambda g: 
    print("Running 1st") or
    (lambda x: 
        print ("Running 2nd") or
        g(g,0, x))
)
entry = buildRecursive (lambda f,current,max:
        print("Running 3rd") or
        print(current) or f(f, current+1, max) if current <= max else None)
entry (100)

This should be equivalent to what you have. This has print statements as the first operation of every call, and you can see that you're only running the 3rd one repeatedly. Essentially, you're generating as few stack frames per iteration as possible, given the constraints as I understand them.

As an aside, after some reading I understand why you're doing the or thing, but coming from other languages, that is downright hideous. It might be the python way of doing things, but it's a pretty awful way of sequencing operations - especially because of short-circuiting logic, so if you have try to bind operations such that the first doesn't return None, your code will mysteriously break. I would suggest using a tuple instead - (firstOp, secondOp) - but I think that will have memory or performance implications as Python will actually build the resulting value.

You might define your own infix operator which will evaluate both left and right operands in order and return the second (or the first... can you feel functional programming calling yet?). However in Python I think this will result in additional stack frames, as the operator will produce its own totally trivial stack frame.

Have you explored languages other than Python? If not I'd say it's time.

Sign up to request clarification or add additional context in comments.

Comments

0

You could do something like that:

x = lambda x: print("\n".join(map(str, list(range(1,x+1)))))

x(100)

Edit:

You can do it like that:

x = lambda x: print(*range(1,x+1), sep='\n')
x(100)

1 Comment

Sorry, I didn't include this in the question, but I'm looking for a way to do this without delegating the loop logic to another function. I've edited the question with this detail.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.