0

For some reason, to capture of variable like i here requires to define a function then call it. Just using the body of that function does not capture i and, upon invocation, uses 3, the last value used for i.

Is there a better way to capture a variable ? (without superfluous syntactic noise like those function definition/calls)

class Node(object):
    def __init__(self, value, next=None):
        self.value = value
        self.next = next
    
    def __str__(self):
        return str(self.value) + ',' + str(self.next)
    
    def list2LinkedListFoldrImpPb(nums):
      ret = {0:lambda x:x}
      i = 0  
      for num in nums:
        ret[i+1] = lambda rs: ret[i](Node(num, rs))#---- i NOT captured !!
        i = i+1
      return ret[i](None)
    
    def list2LinkedListFoldrImp(nums):
      ret = {0:lambda x:x}
      i = 0
      def setf(ret, i, num):
          ret[i+1] = lambda rs: ret[i](Node(num, rs))
      for num in nums:
        setf(ret, i, num) #---- i captured !!
        i = i+1
      return ret[i](None)
    
    
    print(list2LinkedListFoldrImpPb([5,4,1])) # maximum recursion depth exceeded  !!!
    print(list2LinkedListFoldrImp([5,4,1])) # works 

Solution

For reference, the solution, as mentioned in the duplicate link, is to make sure you list all the variables you intend to capture as a local parameter.

There is NO capture inside a body, scopes/environments - a part of a closure - are created only when calling functions (and default arguments - viewed as part of a call I imagine - are captured)

class Node(object):
  def __init__(self, value, next=None):
    self.value = value
    self.next = next

  def __str__(self):
    return str(self.value) + ',' + str(self.next)

# Creating a function and calling it works
def list2LinkedListFoldrOK(nums):
  ret = {0:lambda x:x}
  i = 0
  def setf(ret, i, num):
      ret[i+1] = lambda rs: ret[i](Node(num, rs))
  for num in nums:
    setf(ret, i, num)
    i = i+1
  return ret[i](None)

#Pb : The i in each lambdas refers to the *last* value that i had in the scope it came from, i.e., 3
def list2LinkedListFoldrImpKO(nums):
  ret = {0:lambda x:x}
  i = 0  
  for num in nums:
    ret[i+1] = lambda rs: ret[i](Node(num, rs))
    i = i+1
  return ret[i](None)

#Solution : List all captured variables as locals via default
def list2LinkedListFoldrImpOK2(nums):
  ret = {0:lambda x:x}
  i = 0  
  for num in nums:
    ret[i+1] = lambda rs, i=i, num=num: ret[i](Node(num, rs))
    i = i+1
  return ret[i](None)

#Solution : or make an actual call 
def list2LinkedListFoldrImpOK3(nums):
  ret = {0:lambda x:x}
  i = 0  
  for num in nums:
    ret[i+1] = (lambda i,num: lambda rs: ret[i](Node(num, rs)))(i,num)
    i = i+1
  return ret[i](None)



print(list2LinkedListFoldrImpOK2([5,4,1]))
print(list2LinkedListFoldrImpOK3([5,4,1]))
print(list2LinkedListFoldrImp([5,4,1]))
nicolas
  • 9,549
  • 3
  • 39
  • 83

1 Answers1

0

say nums is 1,2,3 then

      i = 0  
      for num in nums:
        ret[i+1] = lambda rs: ret[i](Node(num, rs))#---- i NOT captured !!
        i = i+1
      return ret[i](None)

The loop will set ret[0:2] and then return ret[3]

I guess that's why it is not working as expected

try return ret[0:i-1](None)

Vorsprung
  • 32,923
  • 5
  • 39
  • 63