Search code examples
pythonarrayssubtraction

Need to display only the current element of an itertools calculation of a list in Python


When creating a function that subtracts numbers from an element (all user input, and calculates continuously), the compiler only displays all of the elements calculated.

Tried passing the counter as an argument to the subtraction function, but no matter what I try to do with the for loop, I keep receiving index out of bounds errors

def number_list(operator_item): #Creates a list of numbers, after calculation, list is returned to main to be assigned to list_of_numbers variable
    number_list = []
    counter = 1
    print("Enter one value at a time and press enter, press = and enter to proceed")
    while number_list != "=": # Calculations are entered until the user types the equal sign, then the answer is returned to main

        try:
            list_value = float(input())
        except ValueError: # Used an exception to confirm sentinel value, this is to maintain accuracy with float number calculations. No error is handled unless "=" is not entered second time
            sentinel_value = input("Press '=' again to complete calculation and copy answer to system clipboard.\n\n") 
            if sentinel_value == "=":
                copy(running_total)
                return running_total
            else:
                print("Invalid entry!")

        number_list.append(list_value) #Each number that is input will be added to list 
        counter += 1

# I have functions for sum and product, but are excluded for relevance
        if operator_item == "-":
            running_total = subtraction(number_list, counter)
            print("Current difference:", running_total)


def subtraction(number_array, number_element):
    total = list(itertools.accumulate(number_array, operator.sub))
    return total

Here is my actual results from compiling. I'm just subtracting 5 each time.

Enter one value at a time and press enter, press = and enter to proceed

5 (This is the number I enter on the keyboard)

Current difference: [5.0]

5 (This is the number I enter on the keyboard)

Current difference: [5.0, 0.0]

5 (This is the number I enter on the keyboard)

Current difference: [5.0, 0.0, -5.0]

What I was expecting it to do is this:

Enter one value at a time and press enter, press = and enter to proceed

5 (This is the number I enter on the keyboard)

Current difference: 5.0

5 (This is the number I enter on the keyboard)

Current difference: 0.0

5 (This is the number I enter on the keyboard)

Current difference: -5.0

How can I get it to trim the brackets and all preceding elements?


Solution

  • The reason for this behaviour lies within itertools.accumulate. Let's take a look at the docs:

    Make an iterator that returns accumulated sums, or accumulated results of other binary functions (specified via the optional func argument).

    What does this mean? accumulate takes an iterable and a function, and applies this function to the elements of the iterable in a specific order, returning a new iterable containing all the results. First it takes the first (two) element(s), then the result of that and the third element, the result of that and the fourth element, and so on - thats what "accumulating calculation" stands for. A simple example:

    import itertools
    
    def substraction(array):  # adding number_element as an argument is not needed
        total = list(itertools.accumulate(array, lambda x, y: x-y))
        return total
    
    number_array = [5]
    print(substraction(number_array))
    # prints [5]  (5=5)
    
    number_array.append(5)  # array is now [5, 5]
    print(substraction(number_array))
    # prints [5, 0]  (5=5, 5-5=0)
    
    number_array.extend([-5, 4, 6])  # array is now [5, 5, -5, 4, 6]
    print(substraction(number_array))
    # prints [5, 0, 5, 1, -5]  (5=5, 5-5=0, 0-(-5)=5, 5-4=1, 1-6=-5)
    

    Now, from what i understand, you are only interested in the last value of that calculation at any point. There is a module named functoolsthat has a handy functionality for just that, functools.reduce:

    Apply function of two arguments cumulatively to the items of sequence, from left to right, so as to reduce the sequence to a single value.

    So again, it will cumulatively substract the numbers from left to right, but only return the current final result as an integer, which is exactly what you want.

    import functools
    
    def substraction(array):
        total = functools.reduce(lambda x, y: x - y, array)  # notice the reversed order of arguments, compared to itertools.accumulate
        return total
    
    number_array = [5]
    print(substraction(number_array))
    # prints 5 (5=5)
    
    number_array.append(5)
    print(substraction(number_array))
    # prints 0 ((5)-5=0)
    
    number_array.extend([-5, 4, 6])
    print(substraction(number_array))
    # prints -5  (((((5)-5)-(-5))-4)-6=-5)