0

I have this:

import constraint

p = constraint.Problem()

t = [0,5]
c = [3,7]

s = range(len(t))
n = 12

p.addVariables([str(i) for i in s], range(n))

p.addConstraint(lambda x: (x+t[0])%n in c, ('0'))                              
p.addConstraint(lambda x: (x+t[1])%n in c, ('1'))                              

l = [list(i.values()) for i in p.getSolutions()]

print(l)

And that outputs:

[[7, 10], [7, 2], [3, 10], [3, 2]]

But I want to add the constraints in a loop, so I did this instead of the two p.addConstraint lines:

for i in s:
    p.addConstraint(lambda x: (x+t[i])%n in c, (str(i)))

I expected this to give the same output, but instead I got this:

[[10, 10], [10, 2], [2, 10], [2, 2]]    

What am I missing?

klutt
  • 30,332
  • 17
  • 55
  • 95
  • 1
    Related: [Creating lambda inside a loop](https://stackoverflow.com/q/7546285/9997212) – enzo Jun 14 '21 at 20:55
  • Does this answer your question? [Creating lambda inside a loop](https://stackoverflow.com/questions/7546285/creating-lambda-inside-a-loop). The variables used inside the lambda aren't bound to the lambda. Instead, they're evaluated when the lambda is executed, and by the time the lambdas are called, `i` is the last element of `s`. [This answer](https://stackoverflow.com/a/7546960/843953) explains it quite well. – Pranav Hosangadi Jun 14 '21 at 21:21

2 Answers2

2

You can use a function that creates a function and then create the functions in a for loop and add them as constrains.

import constraint

p = constraint.Problem()

t = [0,5]
c = [3,7]

s = range(len(t))
n = 12

p.addVariables([str(i) for i in s], range(n))

def generate_constrained_func(t, n, c):
    def constraint_func(x):
        return ((x+t)%n in c)
    return constraint_func

for idx, t_constraint in enumerate(t):
    p.addConstraint(generate_constrained_func(t_constraint, n, c), (str(idx)))
    
l = [list(i.values()) for i in p.getSolutions()]

print(l)

Output:

[[7, 10], [7, 2], [3, 10], [3, 2]]
0

I did manage to solve it with eval, but I've heard that's a dangerous function so I'm trying to avoid it. But if no one comes up with anything better, that's what I use:

for i in s:
    eval('p.addConstraint(lambda x: (x+t[%d])%%n in c, (str(i)))' % i)

It does not explain why the code in the question does not work, and I'm still curious about it.

klutt
  • 30,332
  • 17
  • 55
  • 95