Re: [Tutor] please return flys in ointment

2013-07-09 Thread Steven D'Aprano

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

2013-07-08 Thread Steven D'Aprano
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

2013-07-08 Thread Jim Mooney
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

2013-07-08 Thread Dave Angel

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

2013-07-08 Thread Jim Mooney
 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

2013-07-08 Thread Steven D'Aprano

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

2013-07-08 Thread Steven D'Aprano

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

2013-07-07 Thread Dave Angel

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

2013-07-07 Thread Alan Gauld

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

2013-07-06 Thread Jim Mooney
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

2013-07-06 Thread Dave Angel

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

2013-07-06 Thread Alan Gauld

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

2013-07-06 Thread Dave Angel

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

2013-07-06 Thread Jim Mooney
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

2013-07-06 Thread Jim Mooney
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

2013-07-06 Thread ALAN GAULD

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

2013-07-06 Thread Jim Mooney
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

2013-07-06 Thread Steven D'Aprano

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

2013-07-06 Thread Dave Angel

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

2013-07-06 Thread Dave Angel

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

2013-07-06 Thread Dave Angel

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