How can i solve a problem by using the outcome of a function as a variable in the following?

101 Views Asked by At

I have come up with this code:

lista= [5,3.2, 'Error', 44, 'Error', 35]
listb= [70, 70, 20, 410,'Error', 4.9]
for i in range(6):
    a= lista[i]
    b= listb[i]
    if a == 'Error' or b == 'Error':
        print(f" Case {i}")
        print(" One of the values is not a number")
        print("-----")
    else:
        def c(a):      
            c=0.1*a**3 -0.6*a**2 
            return c
            def d(a,b,c):       
                return a+b+c(a)
        print(f" Case {i} ")
        print(f" Initial values {a} {b}")
        print(f" Final values {c(a)} {d(a,b,c)}")
        print("-----")``

The problem is the second function, which doesn't work. If i erase everything with d and function number two, it's exactly the same as the desired output. The error i get is: supported operand type(s) for +: 'int' and 'function' Can somebody please help me? The desired output:

Case 0 Initial values 5 70 Final values -2.5 72.5

Case 1 Initial values 3.2 70 Final values -2.8672 70.3328

Case 2 One of the values is not a number

Case 3 Initial values 44 410 Final values 7356.799999999999 7810.799999999999

Case 4 One of the values is not a number Case 5 Initial values 35.0 4.9 Final values 3552.5 3592.4

The number of active lines can't be more than 20.

4

There are 4 best solutions below

0
Oskar Hofmann On BEST ANSWER

Your current code is not correct Python code due to the function d() being defined within the function c() and it is therefore not visible outside of c(). By fixing the indentation of function d(), I get

lista= [5,3.2, 'Error', 44, 'Error', 35]
listb= [70, 70, 20, 410,'Error', 4.9]
for i in range(6):
    a= lista[i]
    b= listb[i]
    if a == 'Error' or b == 'Error':
        print(f" Case {i}")
        print(" One of the values is not a number")
        print("-----")
    else:
        def c(a):      
            c=0.1*a**3 -0.6*a**2 
            return c
        def d(a,b,c):       
            return a+b+c(a)
        print(f" Case {i} ")
        print(f" Initial values {a} {b}")
        print(f" Final values {c(a)} {d(a,b,c)}")
        print("-----")

This code works and also produces your desired output:

Case 0 
 Initial values 5 70
 Final values -2.5 72.5
-----
 Case 1 
 Initial values 3.2 70
 Final values -2.8672 70.3328
-----
 Case 2
 One of the values is not a number
-----
 Case 3 
 Initial values 44 410
 Final values 7356.799999999999 7810.799999999999
-----
 Case 4
 One of the values is not a number
-----
 Case 5 
 Initial values 35 4.9
 Final values 3552.5 3592.4
-----

I would generally avoid defining functions withing loops and move c() and d() outside of the loop:

def c(a):      
    c=0.1*a**3 -0.6*a**2 
    return c
def d(a,b,c):       
    return a+b+c(a)

lista= [5,3.2, 'Error', 44, 'Error', 35]
listb= [70, 70, 20, 410,'Error', 4.9]

for i in range(6):
    a= lista[i]
    b= listb[i]
    if a == 'Error' or b == 'Error':
        print(f" Case {i}")
        print(" One of the values is not a number")
        print("-----")
    else:
        print(f" Case {i} ")
        print(f" Initial values {a} {b}")
        print(f" Final values {c(a)} {d(a,b,c)}")
        print("-----")

Also try to avoid function names like c and d especially when you also use c as a variable. You save a little bit of typing and loose a lot of readability and are also prone to errors when mixing up functions and variables. Single letter variables and functions should almost always be avoided, except when they stand for established natural constants (c = speed of light, and even then it is debatable) or established variable names for coordinates like x, y, z.

3
Nicholas Louckes On

I would look at the scope of the variables being used. Maybe define your functions before the for loop, and make sure to define 'c' prior to 'd'.

#initialize vars
lista= [5,3.2, 'Error', 44, 'Error', 35]
listb= [70, 70, 20, 410,'Error', 4.9]

#functions
def c(a):
  c=0.1*a**3 -0.6*a**2 
  return float(c)
  
def d(a, b, c):       
  return a+b+c(a)

#loop
for i in range(6):
    a= lista[i]
    b= listb[i]
    if a == 'Error' or b == 'Error':
        print(f" Case {i}")
        print(" One of the values is not a number")
        print("-----")
    else:
        print(f" Case {i} ")
        print(f" Initial values {a} {b}")
        print(f" Final values {c(a)} {d(a,b,c)}")
        print("-----")
0
Hai Vu On

By using nested functions, you make your calculation more complicated than necessary. Also, function d is hidden inside function c, so it is not available to your code.

Here is how I would approach this problem:

lista = [5, 3.2, "Error", 44, "Error", 35]
listb = [70, 70, 20, 410, "Error", 4.9]
for i, (a, b) in enumerate(zip(lista, listb)):
    if a == "Error" or b == "Error":
        print(f" Case {i}")
        print(" One of the values is not a number")
        print("-----")
    else:
        print(f" Case {i} ")
        print(f" Initial values {a} {b}")
        c = 0.1 * a**3 - 0.6 * a**2
        d = a + b + c
        print(f" Final values {c} {d}")
        print("-----")

Note that I used zip, which returns values a and b without the need for such look up as lista[i]. I also use enumerate to provide the case number, i.

0
JonSG On

While there are some great answer here and I particularly like the answer by @hai-vu. Building off of it, I want to try to address what I think might be the core of your query, that is

Should I pass a result of calling c(a) or the method c when calling d()

There is nothing really wrong with either approach (passing the method or the result of calling the method to d() other than if you know the result of calling c() already then passing c() to recalculate it is extra work. Being able to pass a method can be very useful though so let's take a closer look at that. If you do pass the method and want to avoid duplicate work, then perhaps d() could return a tuple

def get_d(a, b, fn_get_c):
    c = fn_get_c(a)
    d = a + b + c
    return d, c

Here is what I might start with

def get_c(a):      
    c = 0.1 * a**3 - 0.6 * a**2 
    return c

def get_d(a, b, fn_get_c):
    c = fn_get_c(a)
    d = a + b + c
    return d, c

lista= [5, 3.2, 'Error', 44, 'Error', 35]
listb= [70, 70, 20, 410, 'Error', 4.9]
for i, (a, b) in enumerate(zip(lista, listb)):
    print(f"Case: {i}")

    if "Error" in [a, b]:
        print(f"\tOne of the values ({a}, {b}) is not a number")
    else:
        d, c = get_d(a, b, get_c)
        print(f"\tInitial values: ({a}, {b})")
        print(f"\tFinal values: ({c}, {d})")

    print("-----")