user7526205
user7526205

Reputation: 35

Python: lists, mutation, recursion issue

What I'm trying to do is create a final function inbounds that consumes a list of int (values) and two integers lower and upper. It produces an integer of the total numbers in values that are less than lower and greater than upper. It also mutates values. If there is an int in values that is less than lower it will mutate that value to lower and is there is an int in values that is greater than upper it will mutate that value to upper. ex: listin=[-3,82,105,86,-10,119,100,70] inbounds (listint, 0, 100) => 4, and v is mutated to [0,82,100,86,0,100,100,70].

So I have tried two things so far:

The first thing I tried is:

def inbounds(values, lower, upper):
    if values == []:
        return 0
    elif lower <= values[0] <= upper:
        return inbounds(values[1:], lower, upper)
    elif lower > values[0]:
        values[0] = lower
        return inbounds(values[1:], lower, upper) + 1
    else:
        values[0] = upper
        return inbounds(values[1:],lower, upper) + 1

This does return 4 but the problem with this is that I realized it would only mutate values[0] so then I tried my 2nd attempt to solve this by creating another function that has pos instead of 0 and I add 1 to pos each time it recurses:

def inbounds_from(values, lower, upper, pos):
    if pos < len(values):
        return 0
    elif lower <= values[pos] <= upper:
        return inbounds(values, lower, upper, pos+1)
    elif lower > values[pos]:
        values[pos] = lower
        return inbounds(values, lower, upper, pos+1) + 1
    else:
        values[pos] = upper
        return inbounds(values,lower, upper, pos+1) + 1    

def inbounds(values, lower, upper):
    inbounds_from(values, lower, upper, 0)

Issue with is is that inbounds does nothing!!? Why? I don't get 4 when I test the example and I get the original inlist without any mutations...

edit: Also I tried changing the base case of pos <= len(values) and that still doesn't work

Upvotes: 0

Views: 778

Answers (2)

Marat
Marat

Reputation: 15738

Problem is in the first condition. if pos < len(values) should be pos >= len(values) instead.

Another approach will be to use list comprehensions:

count = sum(upper <= v <= lower for v in values)
values = [min(upper, max(v, lower)) for v in values]  # mutate

Upvotes: 1

juanpa.arrivillaga
juanpa.arrivillaga

Reputation: 96171

There were few problems with your second attempt. Your base-case is wrong - it should be if pos >= len(values). Also, you were recursing on the wrapper function, not the helper/inside function. Finally, you need to return the call to your helper function in your wrapper function. See the following:

In [4]: def inbounds_from(values, lower, upper, pos):
   ...:     if pos >= len(values):
   ...:         return 0
   ...:     elif lower <= values[pos] <= upper:
   ...:         return inbounds_from(values, lower, upper, pos+1)
   ...:     elif lower > values[pos]:
   ...:         values[pos] = lower
   ...:         return inbounds_from(values, lower, upper, pos+1) + 1
   ...:     else:
   ...:         values[pos] = upper
   ...:         return inbounds_from(values,lower, upper, pos+1) + 1
   ...:
   ...: def inbounds(values, lower, upper):
   ...:     return inbounds_from(values, lower, upper, 0)
   ...:

In [5]: test = [-3,82,105,86,-10,119,100,70]

In [6]: inbounds(test, 0, 100)
Out[6]: 4

In [7]: test
Out[7]: [0, 82, 100, 86, 0, 100, 100, 70]

However, in general, recursion is to be avoided in Python. Here is a Pythonic way borrowing Marat's clever min(max()) trick:

In [8]: def in_bounds_iter(values, lower, upper):
   ...:     count = 0
   ...:     for i, val in enumerate(values):
   ...:         if not(lower <=  val <= upper):
   ...:             count += 1
   ...:             values[i] = min(upper, max(val, lower))
   ...:     return count
   ...:

In [9]: test = [-3,82,105,86,-10,119,100,70]

In [10]: in_bounds_iter(test, 0, 100)
Out[10]: 4

In [11]: test
Out[11]: [0, 82, 100, 86, 0, 100, 100, 70]

Upvotes: 0

Related Questions