Python Forum
My program subtracts fractions, but for some reason isn't simplifying them
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
My program subtracts fractions, but for some reason isn't simplifying them
#1
I'm trying to grab numbers from a couple of short lists, turn them into fractions, subtract the fractions, and then simplify them:
#!/usr/bin/env python3
#codeTournamentChallenge2.py

from fractions import Fraction

def fractionSubtraction(a, b):

    #Fraction 

    a = [7, 10]#First fraction = 7/10
    b = [3, 10]#Next fraction = 3/10

    #goal: simplify "7/10 - 3/10" as much as possible
    
    numerator = 0
    denominator = 0
    if a[1] == b[1]:#if the fractions have the same denominator
        denominator = a[1]
        numerator = a[0] - b[0]
        while ((numerator % 2 != 0) and (denominator % 2 != 0)):
            numerator / 2
            denominator / 2
        print("[" + str(numerator) + ", " + str(denominator) + "]")
    
    subtracted = ((a[0] / a[1]) - (b[0] / b[1]))
    #print(str(Fraction(subtracted)))
    #print((subtracted).as_integer_ratio())
    return subtracted

def main():
    a = [7, 10]
    b = [3, 10]
    fractionSubtraction(a, b)
    
main()
the output is:
== RESTART: I:\Python\Python36-32\SamsPrograms\codeTournamentChallenge2.py ==
[4, 10]
>>> 
But why isn't my while loop simplifying them? Is there something wrong with my logic?
Reply
#2
First the expression numerator / 2 doesn't change the numerator. If you want to divide the numerator by 2, you need to write numerator /= 2

That said, the math is not correct, because the test numerator % 2 != 0 will succeed if and only if numerator is odd. You need to write numerator % 2 == 0 instead.

Even with this, there is some more work to do to simplify the fraction: in the general case, you need to find the greatest common divisor of the numerator and the denominator. This is done with Euclid's algorithm.
Reply
#3
Another thing. I don't understand why the following code works:
#!/usr/bin/env python3
#codeTournamentChallenge2.py

from fractions import Fraction

def fractionSubtraction(a, b):

    #Fraction 

##    a = [7, 10]#First fraction = 7/10
##    b = [3, 10]#Next fraction = 3/10

    #goal: simplify "7/10 - 3/10" as much as possible
    
    numerator = 0
    denominator = 0
    if a[1] == b[1]:#if the fractions have the same denominator
        denominator = a[1]
        numerator = a[0] - b[0]
        while ((numerator % 2 == 0) and (denominator % 2 == 0)):
            numerator /= 2
            denominator /= 2
        print("[" + str(numerator) + ", " + str(denominator) + "]")
    
    subtracted = ((a[0] / a[1]) - (b[0] / b[1]))
    #print(str(Fraction(subtracted)))
    #print((subtracted).as_integer_ratio())
    return subtracted

def main():
    a = [7, 10]
    b = [3, 10]
    fractionSubtraction(a, b)
    
main()
Outputting:
== RESTART: I:\Python\Python36-32\SamsPrograms\codeTournamentChallenge2.py ==
[2.0, 5.0]
While this code:
#!/usr/bin/env python3
#codeTournamentChallenge2.py

from fractions import Fraction

def fractionSubtraction(a, b):

    #Fraction 

    a = [7, 10]#First fraction = 7/10
    b = [3, 10]#Next fraction = 3/10

    #goal: simplify "7/10 - 3/10" as much as possible
    
    numerator = 0
    denominator = 0
    if a[1] == b[1]:#if the fractions have the same denominator
        denominator = a[1]
        numerator = a[0] - b[0]
        while ((numerator % 2 == 0) and (denominator % 2 == 0)):
            numerator /= 2
            denominator /= 2
        print("[" + str(numerator) + ", " + str(denominator) + "]")
    
    subtracted = ((a[0] / a[1]) - (b[0] / b[1]))
    #print(str(Fraction(subtracted)))
    #print((subtracted).as_integer_ratio())
    return subtracted

def main():
##    a = [7, 10]
##    b = [3, 10]
    fractionSubtraction(a, b)
    
main()
Outputs the error:
Error:
== RESTART: I:\Python\Python36-32\SamsPrograms\codeTournamentChallenge2.py == Traceback (most recent call last): File "I:\Python\Python36-32\SamsPrograms\codeTournamentChallenge2.py", line 35, in <module> main() File "I:\Python\Python36-32\SamsPrograms\codeTournamentChallenge2.py", line 33, in main fractionSubtraction(a, b) NameError: name 'a' is not defined >>>
Why do the lists have to be defined in the main function? I mean, the (a, b) arguments are included in both the function call on line 33 and the function definition on line 6. It seems more logical that the program should work if the lists were defined in the fractionSubtraction function that actually uses their values. What's is up with that?
Reply
#4
(Feb-01-2018, 09:38 PM)RedSkeleton007 Wrote: It seems more logical that the program should work if the lists were defined in the fractionSubtraction function that actually uses their values.
The lists a and b are parameters in fractionSubtraction(a, b). The advantage of this is that the function can be called several times with different values. For example you could havei
def main():
    fractionSutraction([7, 10], [3, 10])
    fractionSutraction([7, 22], [3, 144])
This is not possible if the lists are defined in fractionSutraction(). In your last code, the names a and b are used in function main() at line 33 but they don't exist in this namespace because a and b have not been defined, so python refuses to execute that statement. A definition inside a function's body, such as line 10 and 11 does not change the value of any variable named 'a' and 'b' outside the function's body.
Reply
#5
(Feb-01-2018, 11:11 PM)Gribouillis Wrote: A definition inside a function's body, such as line 10 and 11 does not change the value of any variable named 'a' and 'b' outside the function's body.
So (a, b) in Line 6:
def fractionSubtraction(a, b):
synchronizes with (a, b) in Line 33:
fractionSubtraction(a, b)
So that 'a' and 'b' can be manipulated within the fractionSubtraction function's definition?

Even if that is the case, I still don't see why that's a smart way of doing things. How is that concept NOT ass-backwards? I thought the purpose of a main function was to call a bunch of other functions in order, so that you can manage the code of your program in an organized and object oriented fashion.

Please help me understand. Why is it such a good and wonderful thing that you're always forced to declare and initialize your variables in the main function instead of declaring and initializing them within the fractionSubtraction function definition that is responsible for manipulating their values in the first place anyway? I mean, doesn't declaring and initializing 'a' and 'b' in the main function definition make them global? Whatever happened to the concept of avoiding global variables whenever possible because of scope issues?
Reply
#6
A function such as fractionSubtraction is similar to a food processor. Calling the function is filling it with vegetables (the arguments), pushing the button and getting back the chopped vegetables (the return value). Obviously the vegetables are not parts of the food processor, they are temporarily necessary.

So the main function prepares the ingredients (a and b), operates the food processor and normally gets a result. In your case, you don't get a return value because instead of returning a value, the functions prints something. This is called a side effect. Your function would be better if it returned the fraction that it computes and this result could be printed by the main function.

Experience will tell you what is the "better" design. This is related to many things: whether the function does one task or several tasks, whether its code is short or not, whether it can be called in many different situations, whether it can be modified easily, etc.

Concerning variables, if a and b are defined in main()'s body, it doesn't make them global variables. They are local variables in the function main(). By the way there is no special function main() in python. Your code doesn't need one, the interpreter doesn't treat it differently from function spameggsham()
Reply
#7
Gribouillis, I loved your food processor explanation so much that I decided to make a program like it!
However, the foodProcessor function doesn't know about the leftover list:

#!/usr/bin/env python3
#NewMainFunctionBasics.py
 
def foodProcessor(tomatoes, bellPeppers, cucumbers, limes):
    leftoverTomatoes = 0
    if tomatoes > 4:
        leftoverTomatoes = tomatoes - 4
        tomStr = (str(leftoverTomatoes) + "_tomatoes")
        leftovers.append(tomStr)
    leftoverBellPeppers = 0
    if bellPeppers > 3:
        leftoverBellPeppers = bellPeppers - 3 
        peppersStr = (str(leftoverBellPeppers) + "_bellPeppers")
        leftovers.append(pepperStr)
    leftoverCucumbers = 0
    if cucumbers > 3:
        leftoverCucumbers = cucumbers - 3
        cucumbersStr = (str(leftoverCucumbers) + "_cucumbers")
        leftovers.append(cucumbersStr)
    leftoverLimes = 0
    if limes > 1:
        leftoverLimes = limes - 1
        limesStr = (str(leftoverLimes) + "_limes")
        leftovers.append(limesStr)
    print("Ingredients for gazpacho successfully gathered!")
    print("Here are your leftover ingredients.")
    return leftovers
 
def main():
    #the following integers are the quantity we have of each ingredient
    tomatoes = 5#recipe requirements = 4
    bellPeppers = 7#recipe requirements = 3
    cucumbers = 15#recipe requirements = 3
    limes = 3#recipe requirements = 1
    leftovers = []
    
    foodProcessor(tomatoes, bellPeppers, cucumbers, limes)
    print(leftovers)
main()
Error:
>>> ==== RESTART: I:/Python/Python36-32/SamsPrograms/NewMainFunctionBasics.py ==== Traceback (most recent call last): File "I:/Python/Python36-32/SamsPrograms/NewMainFunctionBasics.py", line 39, in <module> main() File "I:/Python/Python36-32/SamsPrograms/NewMainFunctionBasics.py", line 37, in main foodProcessor(tomatoes, bellPeppers, cucumbers, limes) File "I:/Python/Python36-32/SamsPrograms/NewMainFunctionBasics.py", line 9, in foodProcessor leftovers.append(tomStr) NameError: name 'leftovers' is not defined >>>
Do I need to pass the leftover list as an argument or something?
Reply
#8
Error:
NameError: name 'leftovers' is not defined
Quote:Do I need to pass the leftover list as an argument or something?
It seems you answered your own question.
Recommended Tutorials:
Reply
#9
Is there a more shorter, more efficient way to code the foodProcessor function instead of using a bunch of if statements:

#!/usr/bin/env python3
#NewMainFunctionBasics.py
 
def foodProcessor(tomatoes, bellPeppers, cucumbers, limes, leftovers):
    leftoverTomatoes = 0
    if tomatoes > 4:
        leftoverTomatoes = tomatoes - 4
        tomStr = (str(leftoverTomatoes) + "_tomatoes")
        leftovers.append(tomStr)
    leftoverBellPeppers = 0
    if bellPeppers > 3:
        leftoverBellPeppers = bellPeppers - 3 
        pepperStr = (str(leftoverBellPeppers) + "_bellPeppers")
        leftovers.append(pepperStr)
    leftoverCucumbers = 0
    if cucumbers > 3:
        leftoverCucumbers = cucumbers - 3
        cucumbersStr = (str(leftoverCucumbers) + "_cucumbers")
        leftovers.append(cucumbersStr)
    leftoverLimes = 0
    if limes > 1:
        leftoverLimes = limes - 1
        limesStr = (str(leftoverLimes) + "_limes")
        leftovers.append(limesStr)
    print("Ingredients for gazpacho successfully gathered!")
    print("Here are your leftover ingredients.")
    return leftovers
 
def main():
    #the following integers are the quantity we have of each ingredient
    tomatoes = 5#recipe requirements = 4
    bellPeppers = 7#recipe requirements = 3
    cucumbers = 15#recipe requirements = 3
    limes = 3#recipe requirements = 1
    leftovers = []
    
    foodProcessor(tomatoes, bellPeppers, cucumbers, limes, leftovers)
    print(leftovers)
main()
Reply
#10
(Mar-03-2018, 11:01 AM)RedSkeleton007 Wrote: Is there a more shorter, more efficient way to code the foodProcessor function
Yes there are many shorter ways. First you don't need to pass the leftovers argument. You can use the return value. Here is my version
#!/usr/bin/env python3
#NewMainFunctionBasics.py
  
def foodProcessor(tomatoes, bellPeppers, cucumbers, limes):
    ingredients = [
        ('tomatoes', tomatoes, 4),
        ('bellPeppers', bellPeppers, 3),
        ('cucumbers', cucumbers, 3),
        ('limes', limes, 1),
    ]
    leftovers = []
    for name, qty, needed in ingredients:
        if qty < needed:
            raise RuntimeError(('Expected at least', needed, name, 'got only', qty))
        leftovers.append('{} {}'.format(qty - needed, name))
    return leftovers
  
def main():
    #the following integers are the quantity we have of each ingredient
    tomatoes = 5#recipe requirements = 4
    bellPeppers =7#recipe requirements = 3
    cucumbers = 15#recipe requirements = 3
    limes = 3#recipe requirements = 1
     
    leftovers = foodProcessor(tomatoes, bellPeppers, cucumbers, limes)
    print("Ingredients for gazpacho successfully gathered!")
    print("Here are your leftover ingredients.")
    print(', '.join(leftovers))
main()
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  coma separator is printed on a new line for some reason tester_V 4 418 Feb-02-2024, 06:06 PM
Last Post: tester_V
  Reason of the output himanshub7 2 2,138 Apr-28-2020, 10:44 AM
Last Post: TomToad
  Code used to work, not anymore for no apparent reason? chicagoxjl 1 1,848 Jan-08-2020, 05:05 PM
Last Post: jefsummers
  comparing integers and fractions tantony 3 1,962 Oct-02-2019, 04:32 PM
Last Post: tantony
  Help for simplifying code mmk1995 8 4,089 Sep-24-2019, 02:04 PM
Last Post: perfringo
  simplifying a stack of elifs Skaperen 8 4,001 Aug-17-2019, 04:13 AM
Last Post: Skaperen
  Syntax error for a reason I don't understand DanielCook 2 2,406 Aug-07-2019, 11:49 AM
Last Post: buran
  Simplifying my code ilondire05 5 3,691 Jul-21-2019, 03:21 AM
Last Post: scidam
  "invalid syntax" for no apparent reason wardancer84 2 7,098 Oct-03-2018, 11:57 AM
Last Post: wardancer84
  for some reason this isnt working lokchi2017 3 2,658 Aug-06-2018, 12:24 PM
Last Post: perfringo

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020