Newbie - 1st Program

M

Mark Smith

Hi,

I'm trying to learn to program and Python seems to be the best "Starter"
language (I've sat through various C tutorials, but have never used the
language "in anger").
Actually, I think I have all the attributes of a hacker (not cracker) except
for programming ability...I hope to learn...

In trying to think of something to experiment with as my first
thought-of-myself program, I thought back to an algorythm that I once worked
out, which basically returns the number of possible routes in a cross
connect (or switch) given the number of ingress or egress ports.

It seems to work - great - but how would you make this more 'elegant' - I'm
sure it's horrible to an experienced hacker...

# Cross-Connect Calculator

# (c)2003 Mark A Smith
# Calculates the number of possible routes for a cross connect
# given a certain number of ingress & egress ports.

# Get number of ports from user

inputPorts = input("Enter number of Input Ports: ")
outputPorts = input("Enter number of Output Ports: ")


# Main Routine

totalPorts = float(inputPorts) + float(outputPorts)

#Debug statements
#print "Total Ports =", int(totalPorts)
#print "Divisor =", float(totalPorts/2)
#print "Subtraction =", int(totalPorts-1)

crossConnects = (totalPorts / 2) * (totalPorts - 1)

# Output

print "You have", int(crossConnects), "possible connection routes."

Any suggestions?

Thanks

Juglugs

-- This sentence is in Spanish when you are not looking.
 
B

Ben Finney

I'm trying to learn to program and Python seems to be the best
"Starter" language (I've sat through various C tutorials, but have
never used the language "in anger").

Guido van Rossum is actively involved with promoting Python as a
language to use for teaching first-time programmers, so it's good to see
that you think it's appropriate.
It seems to work - great - but how would you make this more 'elegant'
- I'm sure it's horrible to an experienced hacker...

Looks fine to me. Well-chosen names, basic sequential flow.

Probably the first improvement I'd make is to take advantage of the fact
that a Python program can be both a script (run as a command) or module
(imported to other programs) with a single file, heavily promoting code
re-use.

Take the functionality that is potentially useful to other programs, and make
functions and/or classes that implement that. (I've use examples that are
rather trivial for library functions here, but the principle is illustrated.)

Then, have the "main routine" only execute when the program is invoked as a
script.

# Cross-connect calculator

def cross_connects( ports ):
""" Calculate number of possible cross-connects
"""
return ( ports / 2 ) * ( ports - 1 )

def get_ports():
""" Get the number of ports from the user
"""
in_ports = input( "Number of Input Ports: " )
out_ports = input( "Number of Output Ports: " )
return ( in_ports, out_ports )

def total_ports( in_ports, out_ports ):
""" Calculate the total number of ports in the network
"""
return ( in_ports + out_ports )

# Main routine
if( __name__ == '__main__' ):
( input_ports, output_ports ) = get_ports()
num_ports = total_ports( input_ports, output_ports )
num_connects = cross_connects( num_ports )
print "Total number of cross-connects:", num_connects
 
A

Alex Martelli

Mark Smith wrote:
...
It seems to work - great - but how would you make this more 'elegant' -
I'm sure it's horrible to an experienced hacker...

Not half bad, actually! I would suggest one improvement: avoid floating
point when you don't need it. In this case:
totalPorts = float(inputPorts) + float(outputPorts) ...
crossConnects = (totalPorts / 2) * (totalPorts - 1)

you probably inserted the float() calls when you found out that the
crossConnects computation got truncated when totalPorts was an odd
integer. But that's just because you're specifying the computations
in the wrong order there. Try, instead:

totalPorts = inputPorts + outputPorts

crossConnects = (totalPorts * (totalPorts - 1)) / 2

The product "totalPorts * (totalPorts - 1)" is guaranteed to be even
for any integer totalPorts, therefore you don't need to worry about
truncation when you divide it by two.


Incidentally, I'm not sure about your "connection routes" count --
it seems to include the possibility of connecting two output ports
to each other, etc. I guess that may or may not make sense,
electrically and logically, depending on your setup. When a valid
"connect" is just between one output port and one input port, of
course, the number of possible connects is inputPorts*outputPorts.

But this isn't really about Python programming (or any other kind
of programming), of course -- except in that it reminds us that no
matter how well we code, first and foremost we must ensure we're
computing what we really WANT to compute -- and that comments exist
to reassure readers and maintainers of the code that we have taken
this into account (e.g., some note in the leading comment about the
possibility of connecting two "egress ports" directly to each
other would have reassured me, as a reader of the code, that the
computation being performed is indeed correct).


Alex
 
B

Bruno Desthuilliers

Mark said:
Hi,
Ho

(snip)

It seems to work - great - but how would you make this more 'elegant' - I'm
sure it's horrible to an experienced hacker...

Not so bad. Well, for a first program, it's even pretty good. I dont see
anything that would make me scream. But I'm not sure I do qualify as "an
experienced hacker" either !-)

Now I'll take Ben Finney's good advices one step further, and show you
how I'd structure this. You may find the exemple somewhat complex or
confusing, but it may help you or other (well, I hope it will) learn
some of Python's features and common idioms, and how to cleanly
modularize code.

Any question welcome, of course !-)

# Cross-Connect Calculator

# (c)2003 Mark A Smith
# Calculates the number of possible routes for a cross connect
# given a certain number of ingress & egress ports.

# Get number of ports from user

inputPorts = input("Enter number of Input Ports: ")
outputPorts = input("Enter number of Output Ports: ")

You should not use input(). Try entering anything else than a positive
integer value, and you'll know why.

Here is a sample data acquisition code :

def int_input(msg = ""):
while (1):
try:
user_input = raw_input(msg)
except EOFError: # user cancelled with ctrl+d
print "\n"
val = None
break

try:
val = int(user_input)
except ValueError:
# not an integer
print "'%s' is not a valid input\n" % user_input
print "(ctrl+d to cancel)\n"

else:
# value is an int
break

# return an int, or None if user cancelled
return val


BTW, if you expect to reuse your code in a different context (like, for
example, a GUI, or batch processing, etc), you should separate data
acquisition and display from computing. So you would have a function for
computing, and functions for data acquisition and display

this may look like this :

def countConnectionRoutes(inputPorts, outputPorts):
""" countConnectionRoutes()

Compute and return number of possible connection routes
for a given input ports count and a given
output ports count
"""
# NB : you should definitively rewrite this
# according to Alex Martelli's observations
totalPorts = float(inputPorts) + float(outputPorts)
crossConnects = (totalPorts / 2) * (totalPorts - 1)
return int(crossConnects)


# one very nice thing about Python is having functions as
# first class citizens. It allows useful things like passing
# functions as arguments to other functions.

def uiCountConnectionRoutes(getDataFun, displayDataFun):
""" uiCountConnectionRoutes()

Compute number of possible connection routes
Ask the number or in and out ports to the user,
using getDataFun and display result using
displayDataFun

in : getDataFun
a function that takes a message string, and
return an int or None if user cancelled
in : displayDataFun
a functio that take a message string
"""
inputPorts = getDataFun("Enter number of Input Ports: ")
if inputPorts is None:
return

outputPorts = getDataFun("Enter number of Output Ports: ")
if outputPorts is None:
return

crossConnects = countConnectionRoutes(inputPorts, outputPorts)

# I split this in 2 line since it wraps in my newsreader !-)
msg = "You have %d possible connection routes." % crossConnects
displayDataFun(msg)

# a very simple wrapper
def displayData(msg):
print "%s\n" % msg


# a main function to use this module as a program
def main(argv):
# the retun status. It may be useful on some OS
# usually, 0 means 'ok', 1 means 'program error',
# and 2 means 'wrong args'
status = 0

# another fine thing with Python is the possibility
# to define nested functions that can access vars in
# the enclosing function (here, the 'argv' argument)
def usage(msg = ""):
write = sys.stderr.write
if len(msg):
write("%s\n" % msg)
write("usage : %s [nb_input_ports nb_output_ports]\n" % argv[0])
write("if called with no args, values will be asked to the user\n")


# act according to the args passed to the command line
# remember that argv[0] is usually the name of the program

if len(argv) == 3 :
# called with 2 command line args,
# may be used for batch processing

try: # check args validity
inputPorts = int(argv[1])
outputPorts = int(argv[2])
except ValueError:
usage("incorrect args : %s %s" % (argv[1], argv[2]))
status = 2

else: # args ok
crossConnects = countConnectionRoutes(inputPorts, outputPorts)
# may be used for batch processing, so we keep output
# to its minimum
print crossConnects

elif len(argv) == 1:
# no args, so we go for interactive version
uiCountConnectionRoutes(int_input, displayData)

else:
# wrong arg count
usage("incorrect args count : %d" % (len(argv) - 1))
status = 2

return status

# run as a program only if called as a program
# (if this module is imported in another one,
# __name__ will be set to the real name of the module)

if __name__ == '__main__':
import sys
status = main(sys.argv)
sys.exit(status)


It may seems a bit overcomplicated, but this allow you to reuse code in
different contexts. You can use the computation function without any
interactive in/out, or use the 'ui' wrapper function with any functions
that has the right param list and return values, or simply use it as a
program, here again with the choice of passing args to the command line
or using the program interactively.

Note that there are still some points to fix (like user entering
negative int values - try it, it's somewhat funny -, or correcting the
countConnectionRoutes() function), but _this_ is your job !-)

HTH
Bruno
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
474,169
Messages
2,570,920
Members
47,464
Latest member
Bobbylenly

Latest Threads

Top