Re: [Tutor] please return flys in ointment
On 10/07/13 04:05, Jim Mooney wrote: On 8 July 2013 18:16, Steven D'Aprano st...@pearwood.info wrote: I wouldn't even call it an Error, since it's actually a limitation of Jim's code, not an actual error. Good point. But since I'm using try-except on input anyway, largely for int-checking, i figured why not use it? Putting in warn or something like that would just complicate matters. Oh, I'm not saying you shouldn't use an exception. I'm just suggesting that you don't call it a Something-or-other-Error, since *internally* it is not a bad data error, but a my function has a limitation problem. Since the exception is only seen internally, it doesn't really matter though. On the other hand, once you catch the Whatever-the-hell-it-gets-called exception, you treat it as if it were an error, by telling the user to try again. So I'm drawing a *really* fine line between errors and non-errors, and you may freely decide which side of the line to cut. The real point I was hoping you would take from this is that exceptions are not necessarily *errors*. E.g.: TypeError, ValueError, ZeroDivisionError -- all errors. SystemExit, KeyboardInterrupt, StopIteration -- not errors. Correct naming of exceptions are just as important as correct naming of functions and variables. I'm a chicken about input since webmasters have some demented people putting all sorts of odd javascript, PHP, and SQL into input to see if they can wreak some damage ;') :-) My advice is to split your code into two (or more) parts. The engine, so to speak, should try it's best to handle anything you throw at it, although it may raise an exception or run for a very long time. E.g. if you type in 10**(10**100) (a googolplex), Python will try to calculate the answer, and probably fail with a MemoryError after an hour or so, if you are lucky. The second part faces the user, and acts as interface from the (trusted) engine to the (untrusted) user's data, and that's where you put in any additional restrictions validating user data. That may seem like more work up front, but it pays off in the long run. [...] Most American jurors are not told, by power-mad judges, that they can disregard the law, the evidence, and the instructions of the judge, if they feel a law is unfair, and render a verdict of innocent. They are kept in the dark about this right, called Jury Nullification. Completely off-topic, but I think it's unfair to describe them as power-mad because they have respect for the law, and incorrect to describe Jury Nullification as a *right*. A jury has *practical power* to ignore the law, since what goes on behind the closed doors of the jury room is sacrosanct and under American law judges and prosecutors have very limited power to inquire into why a jury decides to convict or not. But that's not a legal right, any more than I have the legal right to slander you provided you don't find out about it. It's more like civil disobedience than an explicit right granted to jurors. But consider that Jury Nullification is a dangerous power to wield. Jury nullification has been used to protect escaped slaves from being returned to their slave owners, and to allow racists to commit racially-motivated murder with impunity. It's a double-edged sword, and there's no reason to assume that justice will always be served by encouraging jurors to ignore the law. -- Steven ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: http://mail.python.org/mailman/listinfo/tutor
Re: [Tutor] please return flys in ointment
Comments on your code inline below. On Sat, Jul 06, 2013 at 02:38:27PM -0700, Jim Mooney wrote: import sys # Data ones = {'1': 'one', '2': 'two', '3': 'three', '4': 'four', '5': 'five', '6': 'six', '7': 'seven', '8': 'eight', '9': 'nine'} tens = {'2': 'twenty', '3': 'thirty', '4': 'forty', '5': 'fifty', '6': 'sixty', '7': 'seventy', '8': 'eighty', '9': 'ninety'} doubles = {'0': 'ten', '1': 'eleven', '2': 'twelve', '3': 'thirteen', '4': 'fourteen', '5': 'fifteen', '6': 'sixteen', '7': 'seventeen', '8': 'eighteen', '9': 'nineteen'} powers_of_1000 = (' thousand', ' million', ' billion', ' trillion', ' quadrillion', ' quintillion', ' sextillion', ' septillion', ' octillion', ' nonillion', ' decillion') '''add these later, and also option for dollars-and-cents ending. 'vigintillion', 'novemdecillion', 'octodecillion', 'septendecillion', 'sexdecillion', 'quindecillion', 'quattuordecillion', 'tredecillion', 'duodecillion', 'undecillion', 'decillion', 'nonillion' ''' # Functions def make_triplets_from_input(): '''Enter a positive integer. A list of triplets in the same order will be returned. Raise ValueError to loop on non-integer input, or return 'zero' trigger-value of 'xxx' if all zeroes are entered. If input is okay, strip leading zeroes, then zero-pad leftmost triplet to three characters, so all slices are triplets. Adjust input for different Python versions.''' while True: try: if sys.version[0:3] == '2.7': numbers_str = original = raw_input('Enter a positive integer, space \ separated if desired.') elif sys.version[0:3] == '3.3': numbers_str = original = input('Enter a positive integer, space \ separated if desired.') A better way to handle this is to perform a check once, outside the function at the top of the file: try: raw_input except NameError: # Probably Python 3. raw_input = input and then just use raw_input inside the function. Or the other way around, if you prefer: try: raw_input except NameError: pass else: input = raw_input Then, inside the function, just unconditionally call: result = input(Prompt, or 'quit' to exit: ) result = result.lower().strip() if result == 'quit': break else: print('Python versions 2.7 and 3.3 only supported') sys.exit() numbers_str = ''.join(numbers_str.split()) if numbers_str == '' or numbers_str == ' ': raise ValueError Since you're splitting on whitespace, and joining with the empty string, you cannot get numbers_str == ' '. Besides, no need to explicitly check for '' here, since the very next line will do so for you: numbers_int = int(numbers_str) if numbers_int == 0: print('Zero') sys.exit() One should not treat 0 to mean I want to quit. numbers_str = str(numbers_int) if len(numbers_str) 36: raise ArithmeticError ArithmeticError??? You're not doing arithmetic, how can this be an arithmetic error? You should be able to handle implausibly/arbitrarily long numbers, just by chaining names (e.g. billion billion billion billion...). If you're going to force some arbitrary limit on the number, and then use an exception for flow-control (as you do here, catching ArithmeticError further on), please use a custom exception so as to not confuse the person reading your code. break except KeyboardInterrupt: print('Program cancelled by user') sys.exit() /me lip curls I'm not sure I like that... except ValueError as err: print(original, is not an integer.) continue except ArithmeticError as err: print(original, is too big.\n999...decillion is max: 10**37-1 or 36 chars \ or 12 groups of 3 chars) leftpad = len(numbers_str) % 3 # zero-pad left, so we get all 3-character triplets if leftpad == 0: leftpad = '' elif leftpad == 2: leftpad = '0' else: leftpad = '00' numbers_str = leftpad + numbers_str leftpad = len(numbers_str) % 3 if leftpad != 0: leftpad = 3-leftpad numbers_str = '0'*leftpad + numbers_str triplets = [numbers_str[x:x+3] for x in range(0,len(numbers_str),3)] return (triplets, len(triplets)) See also the recipe for grouper in the documentation for itertools. -- Steven ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: http://mail.python.org/mailman/listinfo/tutor
Re: [Tutor] please return flys in ointment
On 8 July 2013 00:12, Steven D'Apranocatching ArithmeticError further on), please use a custom exception Well, an Arithmetic Error was better than bubbling up to a totally general one. I'm not sure how do custom error code. Short example, please ;') Jim ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: http://mail.python.org/mailman/listinfo/tutor
Re: [Tutor] please return flys in ointment
On 07/08/2013 01:57 PM, Jim Mooney wrote: On 8 July 2013 00:12, Steven D'Apranocatching ArithmeticError further on), please use a custom exception Well, an Arithmetic Error was better than bubbling up to a totally general one. I'm not sure how do custom error code. Short example, please ;') Jim I think a ValueError might be best. See the description: http://docs.python.org/2/library/exceptions.html#exceptions.ValueError exception ValueError Raised when a built-in operation or function receives an argument that has the right type but an inappropriate value, and the situation is not described by a more precise exception such as IndexError. To make a custom error, first pick another error that's a superset of what you're doing. Then simply derive your error class from it. As a minimum: class TooHugeError (ValueError): pass -- DaveA ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: http://mail.python.org/mailman/listinfo/tutor
Re: [Tutor] please return flys in ointment
To make a custom error, first pick another error that's a superset of what you're doing. Then simply derive your error class from it. As a minimum: class TooHugeError (ValueError): pass Actually, I didn't get to classes yet since I wanted to nail procedural - I'm in no rush - but that looks simple enough. Damn, computer is overheating again. I'll be glad when the AZ heatwave is over so I can get back to more hours on the computer, but Reality intervenes ;') Jim -- DaveA __**_ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: http://mail.python.org/**mailman/listinfo/tutorhttp://mail.python.org/mailman/listinfo/tutor -- Jim Most American jurors are not told, by power-mad judges, that they can disregard the law, the evidence, and the instructions of the judge, if they feel a law is unfair, and render a verdict of innocent. They are kept in the dark about this right, called Jury Nullification. ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: http://mail.python.org/mailman/listinfo/tutor
Re: [Tutor] please return flys in ointment
On 09/07/13 05:47, Dave Angel wrote: On 07/08/2013 01:57 PM, Jim Mooney wrote: On 8 July 2013 00:12, Steven D'Apranocatching ArithmeticError further on), please use a custom exception Well, an Arithmetic Error was better than bubbling up to a totally general one. I'm not sure how do custom error code. Short example, please ;') Jim I think a ValueError might be best. [...] class TooHugeError (ValueError): pass I would normally agree, but in Jim's code the exception is purely being used for internal flow control, it is not being exposed to the caller. So I wouldn't inherit from ValueError, I'd keep this a completely independent exception. I wouldn't even call it an Error, since it's actually a limitation of Jim's code, not an actual error. Also, since Jim's code already catches ValueError, he would need ensure that he catches this *first*, otherwise it will be caught by the ValueError handler: try: raise TooHugeError except TooHugeError: # this must come first ... except ValueError: # otherwise this will catch it ... Whereas if you keep the custom exception independent, the order of except clauses doesn't matter. class TooHuge(Exception): pass try: raise TooHuge except ValueError: print(bad value) except TooHuge: print(caught it) -- Steven ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: http://mail.python.org/mailman/listinfo/tutor
Re: [Tutor] please return flys in ointment
On 09/07/13 05:59, Jim Mooney wrote: To make a custom error, first pick another error that's a superset of what you're doing. Then simply derive your error class from it. As a minimum: class TooHugeError (ValueError): pass Actually, I didn't get to classes yet since I wanted to nail procedural - I'm in no rush - but that looks simple enough. Damn, computer is overheating again. I'll be glad when the AZ heatwave is over so I can get back to more hours on the computer, but Reality intervenes ;') That's what I keep telling my AGW-Denialist friend, but he prefers living in a fantasy world where human beings can inject four Hiroshima A-bombs worth of heat into the oceans and atmosphere every second without consequence. (The actual excess heat accumulated over the last decade is 8000 million million megajoules per year.) Global temperatures were increasing even over the last few years while the solar cycle was cooling, now that the solar cycle is returning to its heating phase, so get used to it, you're experiencing a small glimpse of the future. -- Steven ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: http://mail.python.org/mailman/listinfo/tutor
Re: [Tutor] please return flys in ointment
On 07/07/2013 02:47 AM, Jim Mooney wrote: On 6 July 2013 22:52, Dave Angel da...@davea.name wrote: The other author does not use hyphens or commas in the words. He ends each string with a blank. And he neglected to handle zero. He also is buggy beyond a nonillion-1. I found yet another implementation that does work up to at least a decillion, and it agrees with yours throughout (actually, the testing was done with the same million random probes as what I posted earlier.) However, he also omits the commas and the hyphens. An interesting site is: http://rosettacode.org/wiki/Names_to_numbers which has a bunch of different languages (and programmers) implementing the same concept. Note that the spec didn't say how the words should be laid out, and there are many variations in result between the various implementations. These include not only the hyphens, commas, but also the use of the word 'and' between some parts of the numbers. -- DaveA ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: http://mail.python.org/mailman/listinfo/tutor
Re: [Tutor] please return flys in ointment
On 07/07/13 08:44, Jim Mooney wrote: thought about it, but it seemed to me that when most people are verbalizing big numbers, they tend not to use 'and.' True in the USA but not the UK (and derivatives?). Here most folks seem to put the 'and' in. eg: three thousand, four hundred and fifty six... Incidentally, if you get any Indian users they'll be looking for support for 'laks' - 100,000's :-) HTH -- Alan G Author of the Learn to Program web site http://www.alan-g.me.uk/ ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: http://mail.python.org/mailman/listinfo/tutor
[Tutor] please return flys in ointment
I reworked my numbers_to_name program so I can actually follow the logic. Since I think the best way to learn is to follow something through to completion, I'll put it online so I learn how to do that. But naturally, lest I look dumb, I'd appreciate criticism, improvements of bad form, or if anyone can break it, let me know ;') . Seems to work with a bit of random testing, but ya never know. If you did, Msoft wouldn't be putting out those updates all the time. Works with Py 27 or 33, but that brings up a question: I think most of my programs work with both if I don't do anything exotic, except for input(), which is the fly in the ointment. Instead of using the routine below, could I redefine input() to use the routine, or it that a bad idea? I have a feeling the leftpad routine could be simplified, but it isn't coming to me. # Using C:\Python33\python.exe on Win 7 in c:\python33\jimprogs - not Windows-specific # Also works with python 2.7 import sys # Data ones = {'1': 'one', '2': 'two', '3': 'three', '4': 'four', '5': 'five', '6': 'six', '7': 'seven', '8': 'eight', '9': 'nine'} tens = {'2': 'twenty', '3': 'thirty', '4': 'forty', '5': 'fifty', '6': 'sixty', '7': 'seventy', '8': 'eighty', '9': 'ninety'} doubles = {'0': 'ten', '1': 'eleven', '2': 'twelve', '3': 'thirteen', '4': 'fourteen', '5': 'fifteen', '6': 'sixteen', '7': 'seventeen', '8': 'eighteen', '9': 'nineteen'} powers_of_1000 = (' thousand', ' million', ' billion', ' trillion', ' quadrillion', ' quintillion', ' sextillion', ' septillion', ' octillion', ' nonillion', ' decillion') '''add these later, and also option for dollars-and-cents ending. 'vigintillion', 'novemdecillion', 'octodecillion', 'septendecillion', 'sexdecillion', 'quindecillion', 'quattuordecillion', 'tredecillion', 'duodecillion', 'undecillion', 'decillion', 'nonillion' ''' # Functions def make_triplets_from_input(): '''Enter a positive integer. A list of triplets in the same order will be returned. Raise ValueError to loop on non-integer input, or return 'zero' trigger-value of 'xxx' if all zeroes are entered. If input is okay, strip leading zeroes, then zero-pad leftmost triplet to three characters, so all slices are triplets. Adjust input for different Python versions.''' while True: try: if sys.version[0:3] == '2.7': numbers_str = original = raw_input('Enter a positive integer, space \ separated if desired.') elif sys.version[0:3] == '3.3': numbers_str = original = input('Enter a positive integer, space \ separated if desired.') else: print('Python versions 2.7 and 3.3 only supported') sys.exit() numbers_str = ''.join(numbers_str.split()) if numbers_str == '' or numbers_str == ' ': raise ValueError numbers_int = int(numbers_str) if numbers_int == 0: print('Zero') sys.exit() numbers_str = str(numbers_int) if len(numbers_str) 36: raise ArithmeticError break except KeyboardInterrupt: print('Program cancelled by user') sys.exit() except ValueError as err: print(original, is not an integer.) continue except ArithmeticError as err: print(original, is too big.\n999...decillion is max: 10**37-1 or 36 chars \ or 12 groups of 3 chars) leftpad = len(numbers_str) % 3 # zero-pad left, so we get all 3-character triplets if leftpad == 0: leftpad = '' elif leftpad == 2: leftpad = '0' else: leftpad = '00' numbers_str = leftpad + numbers_str triplets = [numbers_str[x:x+3] for x in range(0,len(numbers_str),3)] return (triplets, len(triplets)) def numbers_to_name(triplets, triplen): '''Create a name from each triplet and append the power of ten''' triplen -= 2 number_name = '' for triplet in triplets: triplet_name = '' first, second, third, last_two = (triplet[0], triplet[1], triplet[2], triplet[1:]) if triplet == '000': triplen -= 1 continue if first '0': if last_two == '00': # special case - snip extra space separator triplet_name += ones.get(first) + ' hundred' else: triplet_name += ones.get(first) + ' hundred ' if second == '0': if third '0': triplet_name += ones.get(third) elif second == '1': triplet_name += doubles.get(third) elif second '1': triplet_name += tens.get(second) if third '0': triplet_name += '-' + ones.get(third) number_name += triplet_name if triplen -1: number_name += powers_of_1000[triplen] + ', ' triplen -= 1 if number_name[-2] == ',': number_name = number_name[0:-2] # special case - snip extraneous ending comma return
Re: [Tutor] please return flys in ointment
On 07/06/2013 05:38 PM, Jim Mooney wrote: I reworked my numbers_to_name program so I can actually follow the logic. Since I think the best way to learn is to follow something through to completion, I'll put it online so I learn how to do that. But naturally, lest I look dumb, I'd appreciate criticism, improvements of bad form, or if anyone can break it, let me know ;') . Seems to work with a bit of random testing, but ya never know. If you did, Msoft wouldn't be putting out those updates all the time. Works with Py 27 or 33, but that brings up a question: I think most of my programs work with both if I don't do anything exotic, except for input(), which is the fly in the ointment. Instead of using the routine below, could I redefine input() to use the routine, or it that a bad idea? I have a feeling the leftpad routine could be simplified, but it isn't coming to me. General comments: 1) several lines wrapped in the newreader, so I had to paste things together. Presumably this won't happen whereever you put it online, but you should make sure and check after uploading it to make sure no such problems were introduced. Also, instead of using the backslash line-continuation character, I generally try to use the syntax of the language instead. So if you have a quote which won't fit on one line, but you don't want embedded newlines in it either, you can use the implicit concatenation of adjacent literals. Frequently you have to put parens around the whole thing to make sure the compiler sees it as one line. But I think it's usually more readable that way. First example: the prompt in the input/raw_input lines. # Using C:\Python33\python.exe on Win 7 in c:\python33\jimprogs - not Windows-specific # Also works with python 2.7 You always take the data in by input/raw_input. Why not permit a value on the command line? And even more important, you're mixing input/output with algorithm here. import sys # Data ones = {'1': 'one', '2': 'two', '3': 'three', '4': 'four', '5': 'five', '6': 'six', '7': 'seven', '8': 'eight', '9': 'nine'} tens = {'2': 'twenty', '3': 'thirty', '4': 'forty', '5': 'fifty', '6': 'sixty', '7': 'seventy', '8': 'eighty', '9': 'ninety'} doubles = {'0': 'ten', '1': 'eleven', '2': 'twelve', '3': 'thirteen', '4': 'fourteen', '5': 'fifteen', '6': 'sixteen', '7': 'seventeen', '8': 'eighteen', '9': 'nineteen'} powers_of_1000 = (' thousand', ' million', ' billion', ' trillion', ' quadrillion', ' quintillion', ' sextillion', ' septillion', ' octillion', ' nonillion', ' decillion') '''add these later, and also option for dollars-and-cents ending. 'vigintillion', 'novemdecillion', 'octodecillion', 'septendecillion', 'sexdecillion', 'quindecillion', 'quattuordecillion', 'tredecillion', 'duodecillion', 'undecillion', 'decillion', 'nonillion' Those last two you've already implemented ''' # Functions def make_triplets_from_input(): '''Enter a positive integer. A list of triplets in the same order will be returned. Raise ValueError to loop on non-integer input, or return 'zero' trigger-value of 'xxx' if all zeroes are entered. If input is okay, strip leading zeroes, then zero-pad leftmost triplet to three characters, so all slices are triplets. Adjust input for different Python versions.''' while True: try: if sys.version[0:3] == '2.7': I'd do the version checking in initialization code, not here. And I'd do it by redefining input to do what it does in 3.3 numbers_str = original = raw_input('Enter a positive integer, space \ separated if desired.') elif sys.version[0:3] == '3.3': numbers_str = original = input('Enter a positive integer, space \ separated if desired.') Note: you explicitly support zero, so this should say non-negative, rather than positive. I'm not sure why you support spaces between the digits, and not commas, but no biggie. BTW, you also support other white space, like tabs. else: print('Python versions 2.7 and 3.3 only supported') sys.exit() numbers_str = ''.join(numbers_str.split()) if numbers_str == '' or numbers_str == ' ': raise ValueError numbers_int = int(numbers_str) if numbers_int == 0: print('Zero') sys.exit() This is just weird. You're in the middle of an input routine, and you just print something and exit the code?? numbers_str = str(numbers_int) if len(numbers_str) 36: raise ArithmeticError break except KeyboardInterrupt: print('Program cancelled by user') sys.exit() except ValueError as err: print(original, is not an integer.) continue except ArithmeticError as err: print(original, is too big.\n999...decillion is max: 10**37-1 or 36 chars \ or 12
Re: [Tutor] please return flys in ointment
On 06/07/13 22:38, Jim Mooney wrote: naturally, lest I look dumb, I'd appreciate criticism, OK, here are a few comments, take em or leave em... import sys # Data snip... # Functions def make_triplets_from_input(): Why not pass in the prompt string as an argument? '''Enter a positive integer. A list of triplets in the same order will be returned. Raise ValueError to loop on non-integer input, or return 'zero' trigger-value of 'xxx' if all zeroes are entered. If input is okay, strip leading zeroes, then zero-pad leftmost triplet to three characters, so all slices are triplets. Adjust input for different Python versions.''' Not strictly accurate - see comments below. while True: try: if sys.version[0:3] == '2.7': numbers_str = original = raw_input('Enter a positive integer, space \ separated if desired.') I'd probably just use input = raw_input to keep the code simple. elif sys.version[0:3] == '3.3': numbers_str = original = input('Enter a positive integer, space \ separated if desired.') Then you don't really need this bit unless you really want to exclude other v3.X Python's which seems a tad harsh! else: print('Python versions 2.7 and 3.3 only supported') sys.exit() Probably better to take a chance and say Python versions above 2.7. Backward compatibility is pretty good in Python land. numbers_str = ''.join(numbers_str.split()) if numbers_str == '' or numbers_str == ' ': raise ValueError numbers_int = int(numbers_str) Why not just allow the built in Exception to raise itself? int('') Traceback (most recent call last): File stdin, line 1, in module ValueError: invalid literal for int() with base 10: '' if numbers_int == 0: print('Zero') sys.exit() numbers_str = str(numbers_int) Not sure why this is needed, you already got the int from the str so why not use the original string? if len(numbers_str) 36: raise ArithmeticError break Its not really Arithmetic? I'd have said a ValueError... except KeyboardInterrupt: print('Program cancelled by user') sys.exit() Again, what's wrong with the standard error message? except ValueError as err: print(original, is not an integer.) continue except ArithmeticError as err: print(original, is too big.\n999...decillion is max: 10**37-1 or 36 chars \ or 12 groups of 3 chars) OK, Now i see why the ArithmeticError. But you cpuild just have printed the message before raising ValueError? Or even passed the message into ValueError? raise ValueError('Value too big, try again') Traceback (most recent call last): File stdin, line 1, in module ValueError: Value too big, try again It feels like you are trying to do much of Python's job yourself. Let the force be with you... leftpad = len(numbers_str) % 3 # zero-pad left, so we get all 3-character triplets if leftpad == 0: leftpad = '' elif leftpad == 2: leftpad = '0' else: leftpad = '00' Feels like a job for a dictionary and get() - or even a simple tuple index: leftpad = ('','0','00')[leftpad] Personally I prefer not to change types in a single variable like that (probably my C/Pascal background coming out!) so I'd probably do it like this: leftpad = ('','0','00')[ len(numbers_str) % 3 ] Or introduce an extra vareiable - padlen or somesuch... numbers_str = leftpad + numbers_str triplets = [numbers_str[x:x+3] for x in range(0,len(numbers_str),3)] return (triplets, len(triplets)) Not sure I'd bother returning the len, the user can get it as needed very easily and your function name and docstring give no hint to expect a len result as well as the triplets. No further comments - its getting late! :-) HTH -- Alan G Author of the Learn to Program web site http://www.alan-g.me.uk/ ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: http://mail.python.org/mailman/listinfo/tutor
Re: [Tutor] please return flys in ointment
On 07/06/2013 06:34 PM, Dave Angel wrote: On 07/06/2013 05:38 PM, Jim Mooney wrote: I reworked my numbers_to_name program so I can actually follow the logic. Since I think the best way to learn is to follow something through to completion, I'll put it online so I learn how to do that. But naturally, lest I look dumb, I'd appreciate criticism, improvements of bad form, or if anyone can break it, let me know ;') . Seems to work with a bit of random testing, but ya never know. If you did, Msoft wouldn't be putting out those updates all the time. Works with Py 27 or 33, but that brings up a question: I think most of my programs work with both if I don't do anything exotic, except for input(), which is the fly in the ointment. Instead of using the routine below, could I redefine input() to use the routine, or it that a bad idea? I have a feeling the leftpad routine could be simplified, but it isn't coming to me. General comments: 1) several lines wrapped in the newreader, so I had to paste things together. Presumably this won't happen whereever you put it online, but you should make sure and check after uploading it to make sure no such problems were introduced. Also, instead of using the backslash line-continuation character, I generally try to use the syntax of the language instead. So if you have a quote which won't fit on one line, but you don't want embedded newlines in it either, you can use the implicit concatenation of adjacent literals. Frequently you have to put parens around the whole thing to make sure the compiler sees it as one line. But I think it's usually more readable that way. First example: the prompt in the input/raw_input lines. # Using C:\Python33\python.exe on Win 7 in c:\python33\jimprogs - not Windows-specific # Also works with python 2.7 You always take the data in by input/raw_input. Why not permit a value on the command line? And even more important, you're mixing input/output with algorithm here. import sys # Data ones = {'1': 'one', '2': 'two', '3': 'three', '4': 'four', '5': 'five', '6': 'six', '7': 'seven', '8': 'eight', '9': 'nine'} tens = {'2': 'twenty', '3': 'thirty', '4': 'forty', '5': 'fifty', '6': 'sixty', '7': 'seventy', '8': 'eighty', '9': 'ninety'} doubles = {'0': 'ten', '1': 'eleven', '2': 'twelve', '3': 'thirteen', '4': 'fourteen', '5': 'fifteen', '6': 'sixteen', '7': 'seventeen', '8': 'eighteen', '9': 'nineteen'} powers_of_1000 = (' thousand', ' million', ' billion', ' trillion', ' quadrillion', ' quintillion', ' sextillion', ' septillion', ' octillion', ' nonillion', ' decillion') '''add these later, and also option for dollars-and-cents ending. 'vigintillion', 'novemdecillion', 'octodecillion', 'septendecillion', 'sexdecillion', 'quindecillion', 'quattuordecillion', 'tredecillion', 'duodecillion', 'undecillion', 'decillion', 'nonillion' Those last two you've already implemented ''' # Functions def make_triplets_from_input(): '''Enter a positive integer. A list of triplets in the same order will be returned. Raise ValueError to loop on non-integer input, or return 'zero' trigger-value of 'xxx' if all zeroes are entered. If input is okay, strip leading zeroes, then zero-pad leftmost triplet to three characters, so all slices are triplets. Adjust input for different Python versions.''' while True: try: if sys.version[0:3] == '2.7': I'd do the version checking in initialization code, not here. And I'd do it by redefining input to do what it does in 3.3 numbers_str = original = raw_input('Enter a positive integer, space \ separated if desired.') elif sys.version[0:3] == '3.3': numbers_str = original = input('Enter a positive integer, space \ separated if desired.') Note: you explicitly support zero, so this should say non-negative, rather than positive. I'm not sure why you support spaces between the digits, and not commas, but no biggie. BTW, you also support other white space, like tabs. else: print('Python versions 2.7 and 3.3 only supported') sys.exit() numbers_str = ''.join(numbers_str.split()) if numbers_str == '' or numbers_str == ' ': raise ValueError numbers_int = int(numbers_str) if numbers_int == 0: print('Zero') sys.exit() This is just weird. You're in the middle of an input routine, and you just print something and exit the code?? numbers_str = str(numbers_int) if len(numbers_str) 36: raise ArithmeticError break except KeyboardInterrupt: print('Program cancelled by user') sys.exit() except ValueError as err: print(original, is not an integer.) continue except ArithmeticError as err: print(original, is too big.\n999...decillion is max: 10**37-1
Re: [Tutor] please return flys in ointment
On 6 July 2013 15:34, Dave Angel da...@davea.name wrote: General comments: etc,,, Thanks very much. Very detailed. I'll print that out and make changes ;') I was worried about linewrap. I'll have to cut my big margins down and pay more attention to it. As a start, where I print Zero and exit, it seemed natural since a user entering Zero is, I assume, asking for what they want, and they get Zero, finissimo. But I guess I should be generous and figure they mistyped, then loop for better input. I think I already asked (or maybe not) if more than one exit is a good idea, though. -- Jim I asked God for a bike, but I know God doesn't work that way. So I stole a bike and asked for forgiveness. ~ Anonymous ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: http://mail.python.org/mailman/listinfo/tutor
Re: [Tutor] please return flys in ointment
On 6 July 2013 16:06, Alan Gauld alan.ga...@btinternet.com wrote: Probably better to take a chance and say Python versions above 2.7. Backward compatibility is pretty good in Python land. Oddly, I was just looking at pyparsing, and it mentioned it's good for Python 2.3 and above. But that left me thinking Do they mean version 3 and above? since a lot is not ready for version 3. BTW, I understand the basics of regexes, but had some problems and looked into pyparsing. Has anyone used that and does it seem better than regexes, or easier on the noggin? Jim I asked God for a bike, but I know God doesn't work that way. So I stole a bike and asked for forgiveness. ~ Anonymous ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: http://mail.python.org/mailman/listinfo/tutor
Re: [Tutor] please return flys in ointment
Oddly, I was just looking at pyparsing, and it mentioned it's good for Python 2.3 and above. But that left me thinking Do they mean version 3 and above? since a lot is not ready for version 3. Since we know a lot has changed since 2.3 and especially since 2.3 I'd take it with a pinch of salt and consider it v2 safe. But if it says 2.7 and above it should be v3 safge too since v2.7 allows pretty good portability between 2 and 3. But thats just me... BTW, I understand the basics of regexes, but had some problems and looked into pyparsing. Has anyone used that and does it seem better than regexes, or easier on the noggin?When searching for text use simple string searches where possible. If not possible use regexes where the input and/or search string are unstructured. If its structured use a parser for preference. (HTML, XML or pyparser etc for proprietary type stuff) There are a few things where regexes are the best tool, but for anything non-trivial (relatively speaking!) there is usually a better way... IMHO... Alan g.___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: http://mail.python.org/mailman/listinfo/tutor
Re: [Tutor] please return flys in ointment
On 6 July 2013 18:01, ALAN GAULD alan.ga...@btinternet.com wrote: If its structured use a parser for preference. (HTML, XML or pyparser etc for proprietary type stuff Ah, I see - don't bother with a parser unless you have a lot of structure. BTW, I extracted the main function from my program to a test program and it gets an index error after 6,000 or so tests of random integer strings. in case someone finds that, I'm already workin' on it ;') Jim ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: http://mail.python.org/mailman/listinfo/tutor
Re: [Tutor] please return flys in ointment
On 07/07/13 07:38, Jim Mooney wrote: I reworked my numbers_to_name program so I can actually follow the logic. Since I think the best way to learn is to follow something through to completion, I'll put it online so I learn how to do that. But naturally, lest I look dumb, I'd appreciate criticism, improvements of bad form, or if anyone can break it, let me know ;') . Let's look at your overall program design: You should always separate *interface* from *functionality*. As it stands now, your make_triplets_from_input() function does both: it interacts with the user, and performs critical functionality. That means you cannot [easily] either test, or use, that critical functionality *except* interactively. And that brings us to the second issue: with respect, your API (Application Programming Interface) sucks. You have two functions, one to get *a string* of digits directly from the user and convert them to triplets, and one to convert triplets into a string. Nowhere do you have a function to convert *numbers* to names, despite the alleged name of your program. (Programmers who write programs that lie about what they do occupy a special circle of Hell, along with those who talk during movies, people listening to music on their mp3-player so loudly that others can hear it through their headphones, and anyone who wears Lynx/Axe body spray.) So I suggest the following public function: number_to_words: takes an integer and converts to words number_to_words(156) 'one hundred and fifty six' plus some sort of function to handle repeatedly asking the user for a number and converting it. Anything else is just support for the number_to_words function. Thirdly, except for the most throw-away who cares scripts, you should try to design your programs so that they have a separate main() function which only runs when you explicitly run the program, not when you import it. That way this number_to_words function can be reused in other programs, just by importing it. Lastly, you're annoyingly cautious about version numbers. I haven't studied your code in sufficient detail to be sure, but nothing stands out that would prevent your code from working in at least Python 2.5-3.3 inclusive. And when 3.4-3.9 come out over the next decade or so, I would expect your code to continue working. There's no need to *prohibit other versions* -- it's enough to say tested in versions 2.7 and 3.3, and then wait for bug reports to come in: doesn't work in Python 1.5 doesn't work in Python 2.6, here's how to fix it sort of thing. You can then say you won't support anything as old as 1.5 (I don't even consider supporting anything below 2.4) and choose whether or not to accept patches. More to follow... -- Steven ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: http://mail.python.org/mailman/listinfo/tutor
Re: [Tutor] please return flys in ointment
On 07/06/2013 09:18 PM, Jim Mooney wrote: On 6 July 2013 18:01, ALAN GAULD alan.ga...@btinternet.com wrote: If its structured use a parser for preference. (HTML, XML or pyparser etc for proprietary type stuff Ah, I see - don't bother with a parser unless you have a lot of structure. BTW, I extracted the main function from my program to a test program and it gets an index error after 6,000 or so tests of random integer strings. in case someone finds that, I'm already workin' on it ;') Jim If the exception was on the line: first, second, third, last_two = (triplet[0], triplet[1], triplet[2], triplet[1:]) then perhaps you want to apply this fix to the leftpad logic: def make_triplets_from_input(numbers_str): leftpad = 0 * ((-len(numbers_str))%3) numbers_str = leftpad + numbers_str triplets = [numbers_str[x:x+3] for x in range(0,len(numbers_str),3)] return triplets Testing with the following, for j in xrange(1): i = random.randint(0, 10**12) triplets = make_triplets_from_input(str(i)) print(j, i, numbers_to_name(triplets)) After some 800,000 values, I killed it, and put a conditional print instead, printing every ten-thousandth value. (It still calls numbers_to_name(), obviously.) It has done the full hundred million random values without crashing. If you'd like, I'll post my full version, changed as little as possible from yours, but incorporating the suggestions I've tried to make here. In another message, you had the following: As a start, where I print Zero and exit, it seemed natural since a user entering Zero is, I assume, asking for what they want, and they get Zero, finissimo. But I guess I should be generous and figure they mistyped, then loop for better input. I think I already asked (or maybe not) if more than one exit is a good idea, though. You miss my point. The get_raw_input() function should permit a value of 0 but it should NOT just print a value and abort the program. It breaks all reasonable factoring. Conversely, the numbers_to_name() function SHOULD take a value of zero (which will actually be [000] ) and at that point, it's fine to special case. Not by printing Zero, but by returning zero. -- DaveA ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: http://mail.python.org/mailman/listinfo/tutor
Re: [Tutor] please return flys in ointment
On 07/06/2013 10:40 PM, Jim Mooney wrote: On 6 July 2013 19:09, Dave Angel da...@davea.name wrote: SNIP If you'd like, I'll post my full version, changed as little as possible from That would be helpful. The corrections are already four times the size of the program, and at least three pots of coffee ;') I hope this is useful. There are lots more places where I might clean things up, but I wanted to make it as close to yours as possible, so you could reasonably diff the two. # Using C:\Python33\python.exe on Win 7 in c:\python33\jimprogs - not #Windows-specific # Also works with python 2.7 import sys import random #No need to check version, just try to use raw_input. If it's not defined, # then input should already work try: input = raw_input except NameError as e: pass #we must be on version 3.0 or larger try: range = xrange except NameError as e: pass #we must be on version 3.0 or larger # Data ones = {'1': 'one', '2': 'two', '3': 'three', '4': 'four', '5': 'five', '6': 'six', '7': 'seven', '8': 'eight', '9': 'nine'} tens = {'2': 'twenty', '3': 'thirty', '4': 'forty', '5': 'fifty', '6': 'sixty', '7': 'seventy', '8': 'eighty', '9': 'ninety'} doubles = {'0': 'ten', '1': 'eleven', '2': 'twelve', '3': 'thirteen', '4': 'fourteen', '5': 'fifteen', '6': 'sixteen', '7': 'seventeen', '8': 'eighteen', '9': 'nineteen'} powers_of_1000 = (' thousand', ' million', ' billion', ' trillion', ' quadrillion', ' quintillion', ' sextillion', ' septillion', ' octillion', ' nonillion', ' decillion') '''add these later, and also option for dollars-and-cents ending. 'vigintillion', 'novemdecillion', 'octodecillion', 'septendecillion', 'sexdecillion', 'quindecillion', 'quattuordecillion', 'tredecillion', 'duodecillion', 'undecillion', 'decillion', 'nonillion' ''' # Functions def get_raw_input(): '''Enter a positive integer. A list of triplets in the same order will be returned. Raise ValueError to loop on non-integer input, or return 'zero' trigger-value of 'xxx' if all zeroes are entered. If input is okay, strip leading zeroes, then zero-pad leftmost triplet to three characters, so all slices are triplets. Adjust input for different Python versions.''' while True: try: if sys.version[0:3] == '2.7': numbers_str = original = raw_input('Enter a positive integer, space \ separated if desired.') elif sys.version[0:3] == '3.3': numbers_str = original = input('Enter a positive integer, space \ separated if desired.') else: print('Python versions 2.7 and 3.3 only supported') sys.exit() numbers_str = ''.join(numbers_str.split()) if numbers_str == '' or numbers_str == ' ': raise ValueError #NO POINT in special-casing zero. It's a perfectly valid value. #numbers_int = int(numbers_str) #if numbers_int == 0: #print('Zero') #sys.exit() numbers_str = str(int(numbers_str)) #normalize it if len(numbers_str) 36: raise ArithmeticError break except KeyboardInterrupt: print('Program cancelled by user') sys.exit() except ValueError as err: print(original, is not an integer.) continue except ArithmeticError as err: print(original, is too big.\n999...decillion is max: 10**37-1 or 36 chars \ or 12 groups of 3 chars) return numbers_str def make_triplets_from_input(numbers_str): leftpad = 0 * ((-len(numbers_str))%3) numbers_str = leftpad + numbers_str #print numbers_str:, numbers_str triplets = [numbers_str[x:x+3] for x in range(0,len(numbers_str),3)] #print triplets are, triplets return triplets def numbers_to_name(triplets): '''Create a name from each triplet and append the power of ten''' if triplets == [000]: return zero triplen = len(triplets) - 2 number_name = '' #print triplets are, triplets for triplet in triplets: #print triplet is, triplet triplet_name = '' first, second, third, last_two = (triplet[0], triplet[1], triplet[2], triplet[1:]) if triplet == '000': triplen -= 1 continue if first '0': if last_two == '00': # special case - snip extra space separator triplet_name += ones.get(first) + ' hundred' else: triplet_name += ones.get(first) + ' hundred ' if second == '0': if third '0': triplet_name += ones.get(third) elif second == '1': triplet_name += doubles.get(third) elif second '1': triplet_name += tens.get(second) if third '0': triplet_name += '-' + ones.get(third) number_name += triplet_name if triplen -1:
Re: [Tutor] please return flys in ointment
On 07/07/2013 01:30 AM, Dave Angel wrote: On 07/06/2013 10:40 PM, Jim Mooney wrote: On 6 July 2013 19:09, Dave Angel da...@davea.name wrote: SNIP If you'd like, I'll post my full version, changed as little as possible from That would be helpful. The corrections are already four times the size of the program, and at least three pots of coffee ;') I hope this is useful. There are lots more places where I might clean things up, but I wanted to make it as close to yours as possible, so you could reasonably diff the two. Sorry to do this in two parts, but there are a few things I know I added to that code since the last discussion online. (There are undoubtedly others, which is why I suggest you do a diff with your own code, and study the reasons for each difference) As Steve suggested, I added a function int_name() which does what Steven suggested. It encapsulates your entire algorithm, without confusing with input or output schemes. It also has the same interface as the int2word() function I found elsewhere. Main thing is that I found another implementation which I can compare yours to. I didn't copy that code here, because I don't want to contaminate yours with scraps of his. And I didn't read it either, except enough to tell that he has a single useful function that takes an int (long) and returns a string representing the word version of the number. If you look at compare_em(), you'll see that I had to distort both your function and the the int2word() function to make them mostly match up. The other author does not use hyphens or commas in the words. He ends each string with a blank. And he neglected to handle zero. He also is buggy beyond a nonillion-1. So I used this to test your code within those parameters. It won't catch a problem with a very high number, nor one with misplaced or extra commas or dashes. But otherwise, it looks like you two have come very close. I suspect with a little more searching, I could find a different implementation that's closer to yours. BTW, this approach of having two implementations and comparing them is very useful, even if there isn't one out on the net somewhere. When I was microcoding add, subtract, multiply, ... trig, log, ... I also wrote a very crude high precision math package. The slow one was done in a high level language, and very brute force. Then I systematically pitted the two sets of functions against each other, looking for edge cases and bugs. Some things, like the trig functions, I couldn't expect to be precisely the same. (I used things like taylor series for the brute force ones, but with intermediate results carried out to many more places than the accuracy I was actually comparing.) That was in about 1975. -- DaveA ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: http://mail.python.org/mailman/listinfo/tutor