Python Programming
 
Forums: » Register « |  User CP |  Games |  Calendar |  Members |  FAQs |  Sitemap |  Support | 
User Name:
Password:
Remember me
Go Back   Dev Shed ForumsProgramming LanguagesPython Programming

Reply
Add This Thread To:
  Del.icio.us   Digg   Google   Spurl   Blink   Furl   Simpy   Y! MyWeb 
Thread Tools Search this Thread Rate Thread Display Modes
 
Unread Dev Shed Forums Sponsor:
Stop making mediocre tutorials.The best tutorials are video! Camtasia Studio makes it easy to create engaging, buzz-building screen videos at any size, in any popular format. Download the free trial!
  #1  
Old March 21st, 2004, 08:34 PM
DFarist DFarist is offline
Registered User
Dev Shed Newbie (0 - 499 posts)
 
Join Date: Jul 2003
Posts: 16 DFarist User rank is Just a Lowly Private (1 - 20 Reputation Level) 
Time spent in forums: < 1 sec
Reputation Power: 0
Question Mixed numbers and decimals?!?

Okay. I have ran into the first problem in python that I havent been able to solve myself, and I am asking for your help. My problem is that I have created a very simple app that calculates a door cutlist for a cabinet shop. The user inputs the different dimensions of the door and the output tells them what to cut. Now the problem seem to be having is the output doesnt give the correct decimals. If you set the values by hand an not allow for user input it works fine. But using input() seems to throw it out the window.

Also some of the dimensions need to be mixed numbers like 2 and 1/2 inches or 2 1/2 for the user input. But when I try this it gives me and invalid syntax error.

My code is as follows:
Code:
from __future__ import division
q = input("  Number of doors: ")
w = input("            Width: ")
l = input("           Length: ")
c = input("        Cut depth: ")
m = input("Machine clearance: ")
s = input("      Stile width: ")
r = input("       Rail width: ")
sl = l
rl = w-(2*s)+(2*c)
pl = sl-(2*r)-(2*c)-(2*m)
pw = w -(2*s)-(2*c)-(2*m)
rq = 2*q
sq = 2*q
pq = q
print "Cutlist for: ", q, "doors", w, "x", l
print ""
print "     Panels: ", pq, pw, "x", pl
print "     Stiles: ", sq, s, "x", sl
print "      Rails: ", rq, r, "x", rl


Some test inputs for reproduction are:
Number of doors: 1
Width: 12
Length: 12
Cut Depth: 1/8
Machine clearance: 1/32
Stile width: 2.5
Rail width: 2.5

That will give you the output with incorrect decimals.
For the Mixed number error change the 2.5 to 2 1/2


Any suggestions would be greatly appreciated.

Last edited by DFarist : March 21st, 2004 at 10:51 PM.

Reply With Quote
  #2  
Old March 22nd, 2004, 02:37 AM
DevCoach DevCoach is offline
Contributing User
Dev Shed Beginner (1000 - 1499 posts)
 
Join Date: Feb 2004
Location: London, England
Posts: 1,188 DevCoach User rank is Captain (20000 - 30000 Reputation Level)DevCoach User rank is Captain (20000 - 30000 Reputation Level)DevCoach User rank is Captain (20000 - 30000 Reputation Level)DevCoach User rank is Captain (20000 - 30000 Reputation Level)DevCoach User rank is Captain (20000 - 30000 Reputation Level)DevCoach User rank is Captain (20000 - 30000 Reputation Level)DevCoach User rank is Captain (20000 - 30000 Reputation Level)DevCoach User rank is Captain (20000 - 30000 Reputation Level)DevCoach User rank is Captain (20000 - 30000 Reputation Level) 
Time spent in forums: 1 Week 5 Days 11 h 25 m 43 sec
Reputation Power: 251
the problem is that by default the division operator between two integers returns an integer value. This is the same way that languages like C do it, so it has some historical precedent. To get a floating point number, one of the numbers needs to be a float. e.g.

Code:
>>> 1/2
0
>>> 1.0/2
0.5
>>> float(1)/2
0.5
>>> 


However the developers of Python realise this is a problem, and are in the process of changing it. They can't do this all in one go since it will break existing code, so from Python 2.2 this behaviour is deprecated. From version 2.4 the division operator on integers will return a floating point number, which is what you want. If you are using 2.2 or 2.3 then you can get this behaviour now by doing "from __future__ import division" at the start of your code:

Code:
>>> from __future__ import division
>>> 1/2
0.5


See PEP 238 at http://www.python.org/peps/pep-0238.html for the full gory details.

This will fix your problem, but will mean your program will fail if run on versions earlier than 2.2. This may or may not be a problem for you, depending on how you distribute it.

This will also still not allow the user to enter values such as 2 1/2, since that is not a valid Python expression. It would need to be written as 2+1/2 or 2.5.

Also, if you are going to run this on a server then using 'input' is not safe - the user could enter any Python statements, and take control of your system, delete files etc.

The alternative is to write your own expression parser, which is a non-trivial task for a novice programmer but would give complete control over how the user's input is interpreted. If you decide to go this route, let us know and I will post some pointers to writing it.

Regards,

Dave - The Developers' Coach

Reply With Quote
  #3  
Old March 22nd, 2004, 03:40 AM
Grim Archon's Avatar
Grim Archon Grim Archon is offline
Mini me.
Dev Shed Novice (500 - 999 posts)
 
Join Date: Nov 2003
Location: Cambridge, UK
Posts: 783 Grim Archon User rank is Corporal (100 - 500 Reputation Level)Grim Archon User rank is Corporal (100 - 500 Reputation Level)Grim Archon User rank is Corporal (100 - 500 Reputation Level)Grim Archon User rank is Corporal (100 - 500 Reputation Level)  Folding Points: 1488 Folding Title: Novice Folder
Time spent in forums: 3 Days 2 h 15 m 57 sec
Reputation Power: 7
Send a message via MSN to Grim Archon
The problem is a bit more interesting - it seems that (on Python 2.3 at least) the input () function ignores the
from __future__ import division command statement.
Code:
>>> from __future__ import division
>>> 1/2
0.5
>>> input()
1/2
0
>>> a = raw_input()
1/2
>>> a
'1/2'
>>> eval(a.strip())
0.5

However, as you can see above combining raw_input() with eval() seems to avoid the problem.

Grim
__________________
*** Experimental Python Markup CGI V2 ***

Reply With Quote
  #4  
Old March 22nd, 2004, 03:44 AM
Grim Archon's Avatar
Grim Archon Grim Archon is offline
Mini me.
Dev Shed Novice (500 - 999 posts)
 
Join Date: Nov 2003
Location: Cambridge, UK
Posts: 783 Grim Archon User rank is Corporal (100 - 500 Reputation Level)Grim Archon User rank is Corporal (100 - 500 Reputation Level)Grim Archon User rank is Corporal (100 - 500 Reputation Level)Grim Archon User rank is Corporal (100 - 500 Reputation Level)  Folding Points: 1488 Folding Title: Novice Folder
Time spent in forums: 3 Days 2 h 15 m 57 sec
Reputation Power: 7
Send a message via MSN to Grim Archon
My guess - this is an oversight (bug) due the the fact that no one really uses input in code that others will use - because of the security issues.

Reply With Quote
  #5  
Old March 22nd, 2004, 08:59 AM
Grim Archon's Avatar
Grim Archon Grim Archon is offline
Mini me.
Dev Shed Novice (500 - 999 posts)
 
Join Date: Nov 2003
Location: Cambridge, UK
Posts: 783 Grim Archon User rank is Corporal (100 - 500 Reputation Level)Grim Archon User rank is Corporal (100 - 500 Reputation Level)Grim Archon User rank is Corporal (100 - 500 Reputation Level)Grim Archon User rank is Corporal (100 - 500 Reputation Level)  Folding Points: 1488 Folding Title: Novice Folder
Time spent in forums: 3 Days 2 h 15 m 57 sec
Reputation Power: 7
Send a message via MSN to Grim Archon
I don't get enough practice with regular expressions but here's my solution:

It allows input in the form integer, floating point and integer with fraction. The fraction is deliberately constrained to only allow single digit numerators.
Code:
from __future__ import division
import re

imperial = re.compile(r"(\d*\s+\d/\d+)|(\d/\d+)")
metric = re.compile(r"(\d*\.\d+)|(\d+)(?!.*/)")
                    
def NumericInput(prompt="Input value:"): 
    while True: 
        inp = raw_input(prompt).strip()
        match = imperial.match(inp)
        try: 
            if match: 
                val = match.group().replace(" ", "+", 1)
                value = eval(val)
                break
            else: 
                match = metric.match(inp)
                if match: 
                    val = match.group()
                    value = eval(val)
                    break
        except: 
            pass
        print "Input format '1' , '1.5' or '1 1/2', retry..."
    return value

q = NumericInput("  Number of doors: ")
w = NumericInput("            Width: ")
l = NumericInput("           Length: ")
c = NumericInput("        Cut depth: ")
m = NumericInput("Machine clearance: ")
s = NumericInput("      Stile width: ")
r = NumericInput("       Rail width: ")
sl = l
rl = w-(2*s)+(2*c)
pl = sl-(2*r)-(2*c)-(2*m)
pw = w-(2*s)-(2*c)-(2*m)
rq = 2*q
sq = 2*q
pq = q
print "Cutlist for: ", q, "doors", w, "x", l
print ""
print "     Panels: ", pq, pw, "x", pl
print "     Stiles: ", sq, s, "x", sl
print "      Rails: ", rq, r, "x", rl

I'm sure it can be improved on
Grim

Reply With Quote
  #6  
Old March 22nd, 2004, 04:09 PM
DFarist DFarist is offline
Registered User
Dev Shed Newbie (0 - 499 posts)
 
Join Date: Jul 2003
Posts: 16 DFarist User rank is Just a Lowly Private (1 - 20 Reputation Level) 
Time spent in forums: < 1 sec
Reputation Power: 0
Grim Archon. That method worked exactly like I wanted it to. Thank you so much for your help. I have one more question though. Is there any way to get the output to also be in fractions and mixed numbers or am I just asking to much in that line? If you get tired of typing all this just let me know where to find out information, because I have been looking forever. Thanks again.

Reply With Quote
  #7  
Old March 22nd, 2004, 05:06 PM
netytan's Avatar
netytan netytan is offline
Hello World :)
Dev Shed Frequenter (2500 - 2999 posts)
 
Join Date: Mar 2003
Location: Hull, UK
Posts: 2,529 netytan User rank is Second Lieutenant (5000 - 10000 Reputation Level)netytan User rank is Second Lieutenant (5000 - 10000 Reputation Level)netytan User rank is Second Lieutenant (5000 - 10000 Reputation Level)netytan User rank is Second Lieutenant (5000 - 10000 Reputation Level)netytan User rank is Second Lieutenant (5000 - 10000 Reputation Level)netytan User rank is Second Lieutenant (5000 - 10000 Reputation Level)netytan User rank is Second Lieutenant (5000 - 10000 Reputation Level) 
Time spent in forums: 1 Week 2 Days 17 h 18 m 50 sec
Reputation Power: 63
Send a message via ICQ to netytan Send a message via AIM to netytan Send a message via MSN to netytan Send a message via Yahoo to netytan
My function to handle numberic input, enjoy!

Code:
#!/usr/bin/env python

import re

def nuput(prompt = 'Enter a number: '):

    #Parses raw input depending on user input; this can be an int, float or
    #Fraction. If an error occurs while parsing then raise a 'TypeError' for
    #error catching purposes.

    numbers = []

    try:
        for number in raw_input(prompt).split():

                #Loops over each int, float and fraction in the raw_input()
                #call and checks that the input fits given patterns.

                if len(number) > 1:
                        if re.search('^(\d+/\d+)$', number):

                            #Checks if the the string 'number' is a fraction.
                            #Unpacks numbers and assigns the values to 'one'
                            #and 'two'.

                            one, two = map(float, number.split('/'))

                            #Appends a string of float / float to 'numbers'.
                            numbers.append('%f/%f' % (one, two))

                        elif re.search('^(\d*\.\d+)$', number):
                            numbers.append(number)

                elif number.isdigit(): numbers.append(number)

        #Returns an evaluted expression of the values passed to nuput().
        return eval('+'.join(numbers))

    except:
        raise TypeError, 'Input must be Decimal, Float or Fraction.'


Spot an error, let me know.

Mark.
__________________
programming language development: www.netytan.com Hula


Reply With Quote
  #8  
Old March 22nd, 2004, 06:04 PM
Grim Archon's Avatar
Grim Archon Grim Archon is offline
Mini me.
Dev Shed Novice (500 - 999 posts)
 
Join Date: Nov 2003
Location: Cambridge, UK
Posts: 783 Grim Archon User rank is Corporal (100 - 500 Reputation Level)Grim Archon User rank is Corporal (100 - 500 Reputation Level)Grim Archon User rank is Corporal (100 - 500 Reputation Level)Grim Archon User rank is Corporal (100 - 500 Reputation Level)  Folding Points: 1488 Folding Title: Novice Folder
Time spent in forums: 3 Days 2 h 15 m 57 sec
Reputation Power: 7
Send a message via MSN to Grim Archon
It was an fun twist on the temperature conversion problem

This version of the code attempts to match half, 4ths,5ths,8ths,10ths,16ths and 32ths. If it can't then it just prints floating point.
Code:
from __future__ import division
import re

imperial = re.compile(r"(\d*\s+\d/\d+)|(\d/\d+)")
metric = re.compile(r"(\d*\.\d+)|(\d+)(?!.*/)")

#Generate Fractions
fractions = {}
for n in range(1, 32): 
    fractions["%0.5f"%(n/32.0)] = "%s/32"%n
for n in range(1, 16): 
    fractions["%0.5f"%(n/16.0)] = "%s/16"%n
for n in range(1, 10): 
    fractions["%0.5f"%(n/10.0)] = "%s/10"%n
for n in range(1, 8): 
    fractions["%0.5f"%(n/8.0)] = "%s/8"%n
for n in range(1, 5): 
    fractions["%0.5f"%(n/5.0)] = "%s/5"%n
for n in range(1, 4): 
    fractions["%0.5f"%(n/4.0)] = "%s/4"%n
for n in range(1, 3): 
    fractions["%0.5f"%(n/3.0)] = "%s/3"%n
fractions['0.00000'] = ''
fractions["0.50000"] = "1/2"
#End of Fractions

    
def NumericInput(prompt="Input value:"): 
    while True: 
        inp = raw_input(prompt).strip()
        match = imperial.match(inp)
        try: 
            if match: 
                val = match.group().replace(" ", "+", 1)
                value = eval(val)
                break
            else: 
                match = metric.match(inp)
                if match: 
                    val = match.group()
                    value = eval(val)
                    break
        except: 
            pass
        print "Input format '1' , '1.5' or '1 1/2', retry..."
    return value

def ImpPrint(value): 
    neg = ''
    if value<0: 
        neg = '-'
        value = -value
    integer = int(value)
    remainder = "%0.5f"%(value-integer)
    if fractions.has_key(remainder): 
        return "%s%d %s"%(neg, integer, fractions[remainder])
    else: 
        return "%s%d.%s"%(neg, integer, remainder)

    
q = NumericInput("  Number of doors: ")
w = NumericInput("            Width: ")
l = NumericInput("           Length: ")
c = NumericInput("        Cut depth: ")
m = NumericInput("Machine clearance: ")
s = NumericInput("      Stile width: ")
r = NumericInput("       Rail width: ")
sl = l
rl = w-(2*s)+(2*c)
pl = sl-(2*r)-(2*c)-(2*m)
pw = w-(2*s)-(2*c)-(2*m)
rq = 2*q
sq = 2*q
pq = q
print "Cutlist for: %4d doors  %-8s x %-8s"%(q, ImpPrint(w), ImpPrint(l))
print ""
print "     Panels: %4d        %-8s x %-8s"%(pq, ImpPrint(pw), ImpPrint(pl))
print "     Stiles: %4d        %-8s x %-8s"%(sq, ImpPrint(s), ImpPrint(sl))
print "      Rails: %4d        %-8s x %-8s"%(rq, ImpPrint(r), ImpPrint(rl))

If you don't like the fractions I chose (do you use 16ths?) then just modify the fraction generator - (the order is important).

I hope this helps

Grim

Reply With Quote
  #9  
Old March 22nd, 2004, 06:10 PM
Grim Archon's Avatar
Grim Archon Grim Archon is offline
Mini me.
Dev Shed Novice (500 - 999 posts)
 
Join Date: Nov 2003
Location: Cambridge, UK
Posts: 783 Grim Archon User rank is Corporal (100 - 500 Reputation Level)Grim Archon User rank is Corporal (100 - 500 Reputation Level)Grim Archon User rank is Corporal (100 - 500 Reputation Level)Grim Archon User rank is Corporal (100 - 500 Reputation Level)  Folding Points: 1488 Folding Title: Novice Folder
Time spent in forums: 3 Days 2 h 15 m 57 sec
Reputation Power: 7
Send a message via MSN to Grim Archon
Quote:
Originally Posted by netytan
My function to handle numberic input, enjoy!

Code:
#!/usr/bin/env python

import re

def nuput(prompt='Enter a number: '): 

    #Parses raw input depending on user input; this can be an int, float or
    #Fraction. If an error occurs while parsing then raise a 'TypeError' for
    #error catching purposes.

    numbers = []

    try: 
        for number in raw_input(prompt).split(): 

                #Loops over each int, float and fraction in the raw_input()
                #call and checks that the input fits given patterns.

                if len(number)>1: 
                        if re.search('^(\d+/\d+)$', number): 

                            #Checks if the the string 'number' is a fraction.
                            #Unpacks numbers and assigns the values to 'one'
                            #and 'two'.

                            one, two = map(float, number.split('/'))

                            #Appends a string of float / float to 'numbers'.
                            numbers.append('%f/%f'%(one, two))

                        elif re.search('^(\d*\.\d+)$', number): 
                            numbers.append(number)

                elif number.isdigit(): numbers.append(number)

        #Returns an evaluted expression of the values passed to nuput().
        return eval('+'.join(numbers))

    except: 
        raise TypeError, 'Input must be Decimal, Float or Fraction.'


Spot an error, let me know.

Mark.


Nice code Netytan -- in colour

Last edited by netytan : March 22nd, 2004 at 08:01 PM.

Reply With Quote
  #10  
Old March 22nd, 2004, 07:35 PM
DFarist DFarist is offline
Registered User
Dev Shed Newbie (0 - 499 posts)
 
Join Date: Jul 2003
Posts: 16 DFarist User rank is Just a Lowly Private (1 - 20 Reputation Level) 
Time spent in forums: < 1 sec
Reputation Power: 0
Grim, when I ran your code I got an invalid syntax on the

if value<0:

It highlights the semi-colon. I am running Python 2.3 if that has anything to do with it.
Also I hate to ask, but all this stuff looks latin to me. I understand the basic idea of these code snippets but maybe you could help me understand how to do my own. For future reference. Or atleast point me toward some reading material. That would be great.

Again, thanks for all yalls help.

Reply With Quote
  #11  
Old March 22nd, 2004, 07:59 PM
netytan's Avatar
netytan netytan is offline
Hello World :)
Dev Shed Frequenter (2500 - 2999 posts)
 
Join Date: Mar 2003
Location: Hull, UK
Posts: 2,529 netytan User rank is Second Lieutenant (5000 - 10000 Reputation Level)netytan User rank is Second Lieutenant (5000 - 10000 Reputation Level)netytan User rank is Second Lieutenant (5000 - 10000 Reputation Level)netytan User rank is Second Lieutenant (5000 - 10000 Reputation Level)netytan User rank is Second Lieutenant (5000 - 10000 Reputation Level)netytan User rank is Second Lieutenant (5000 - 10000 Reputation Level)netytan User rank is Second Lieutenant (5000 - 10000 Reputation Level) 
Time spent in forums: 1 Week 2 Days 17 h 18 m 50 sec
Reputation Power: 63
Send a message via ICQ to netytan Send a message via AIM to netytan Send a message via MSN to netytan Send a message via Yahoo to netytan
The problem here is that devshed doesnt display html entities, change > with > and < with < and the program should
work fine .

Also, a good place to start would be the Python tutorial at Python.org, what exactly are you looking for?

Mark.

Reply With Quote
  #12  
Old March 22nd, 2004, 08:16 PM
DFarist DFarist is offline
Registered User
Dev Shed Newbie (0 - 499 posts)
 
Join Date: Jul 2003
Posts: 16 DFarist User rank is Just a Lowly Private (1 - 20 Reputation Level) 
Time spent in forums: < 1 sec
Reputation Power: 0
Okay, I tried fixing the code like you said but it gave me a semanic error. Anyway, I will just live with the decimal output because I am amazed that we could get the fractions working. As far as the tutorial goes. I know the basics of Python, so the beginners tutorials wont really help that much. I am looking for things that explain the stuff like:
Code:
imperial = re.compile(r"(\d*\s+\d/\d+)|(\d/\d+)")
metric = re.compile(r"(\d*\.\d+)|(\d+)(?!.*/)")


I have no clue what all those 'd's and 's's mean. I can kinda follow but not very ?comprehendably? Other than that I can follow an reproduce yalls code with the exception of some minor syntax errors because of stupid mistakes. The logic is fine.


Any pointers would be great, but that is what I am mainly confused on.

Thanks

Reply With Quote