Bubble sort in Python not sorting properly - python

I have to implement bubble sort as a homework and my python script has to look for 2 command line parameters:
-f that specifies the file path of the input file that contains a number on each line that I have to sort using bubble sort;
-p that, if specified, tells the script to print the sorted list of numbers in the command line.
Also, I have to implement the algorithm in situ, which means I have to only use one list/array/etc without allocating any other temporary list/array/etc or variable to hold one or a part of all the numbers to sort in the algorithm. So, in my script, I only use unsortedList and nothing else to hold the numbers to sort. I have taken the bubble sort algorithm from the following link: Bubble Sort Homework .
Here is my script:
import sys, getopt
def main(argv):
inputFilePath = ""
printList = False
# Traitement pour parser les arguments
try:
opts, args = getopt.getopt(argv, "f:p")
except getopt.GetoptError:
usage()
sys.exit()
for opt, arg in opts:
if opt in ("-f"):
inputFilePath = arg
if opt in ("-p"):
printList = True
inputFile = open(inputFilePath, "r")
unsortedList = [line.rstrip('\n') for line in inputFile]
sortedList = bubble(unsortedList)
if printList == True:
print (sortedList)
def usage():
print ("""
Usage: bubble.py -f <filepath> -p
-f <filepath> [REQUIRED]: specifies the filepath of the input file
-p [OPTIONAL]: specifies whether to print the sorted list or not
""")
# Function found at https://stackoverflow.com/questions/895371/bubble-sort-homework
def bubble(unsortedList):
length = len(unsortedList) - 1
isSorted = False
while not isSorted:
isSorted = True
for i in range(length):
if unsortedList[i] > unsortedList[i+1]:
isSorted = False
unsortedList[i], unsortedList[i+1] = unsortedList[i+1], unsortedList[i]
return unsortedList
if __name__ == "__main__":
main(sys.argv[1:])
I am having 2 problems with my script:
First, if I do not specify the -f parameter, the script never runs the usage() function, it only tells "No such file or directory: ''". Why isn't my script running the usage() function?
Also, the bubble sort algorithm doesn't seem to work properly. If I run the script, the numbers aren't sorted properly. I can, for example, see 3998 before 403 in the list. But, I have noticed that the numbers ARE sorted, but only from the left of the numbers. For example, I can see 2553, 256, 2562. 256 is clearly not bigger than 2553, but if you take the number from the left, the third character from the left, 6, is bigger than the third character from the left of 2553, which is 5.
How can I solve those two problems?
Thanks for your help.

First, if I do not specify the -f parameter, the script never runs the usage() function, it only tells "No such file or directory: ''". Why isn't my script running the usage() function?
getopt() doesn't know which flags are required and which are optional. It merely checks that you don't pass unspecified flags or omit an argument if a flag requires one with :.
It's up to you to check that -f is passed in if you require it.
Also, the bubble sort algorithm doesn't seem to work properly. If I run the script, the numbers aren't sorted properly. I can, for example, see 3998 before 403 in the list. But, I have noticed that the numbers ARE sorted, but only from the left of the numbers.
That's because your code is actually sorting strings rather than numbers, so it's putting them in lexicographic order. Try converting them to numbers when you read the file in:
unsortedList = [int(line.rstrip('\n')) for line in inputFile]
^^^^ ^
Also, I have to implement the algorithm in situ, which means I have to only use one list/array/etc without allocating any other temporary list/array/etc or variable to hold one or a part of all the numbers to sort in the algorithm.
In that case, I'd recommend removing the return statement from the bubble sort function. The best way to show that you're only using one array is not to create a second variable called sortedList.
bubble(unsortedList)
If your bubble sort call is simply that, without the assignment, then it's clear you must be modifying the original array.

Related

Setting optional system arguments via command prompt

I am doing a project in which I want to specify one system argument on my cmd right after the script.py. My problem is that I want to specify another argument in which is optional, and the user may or may not want to give that argument. Therefore, I am struggling how to deal with the fact that the system argument might or might not be given by the user and how to read that. If everything sounds confusing, the following text might clarify:
The user types the following on the command prompt to run the program:
python script.py file.txt
I want to add an argument which may or may not be given, like:
python script.py file.txt file_added.txt
As I read these arguments on my main script, I though that this problem would solve:
If sys.argv[2] is not None:
file2 = f"\{sys.argv[2]}"
However, I still getting IndexError when doing that. So, is there a simple way to bypass such problem?
If sys.argv holds less than 2 items, you'll get an IndexError. So wrap the statement around with a try block
try:
filename = sys.argv[2]
except IndexError:
filename = None
if filename:
# ... do something
A way to accomplish this would be to check the length of sys.argv. If the length is 3 you'll know that a second argument was passed (3 because the first argument is script.py). So something along the lines:
if len(sys.argv) == 3:
file2 = f"\{sys.argv[2]}"
Here, sys.argv[2] is not None you are checking if 3rd element is None or not and that is the issue.
You are indexing outside the length of argv array and index error.
If you only have max 2 input then you could check the length of argv like if len(sys.argv) == 3 and that means you have got both the input and then you can access them via sys.argv[1] and sys.argv[2]
You can use argsparse which is a built in library in python, which makes it easy to handle command line arguments. Go to the link https://docs.python.org/3/library/argparse.html to know mor, but the basic implementation for your usecase will be like this.
import argparse
parser = argparse.ArgumentParser(description='Enter filenames')
parser.add_argument('-file', type=str,help='enter the file name',dest='filename1')
parser.add_argument('--optional','--op',type=str, dest='filename2',help='enter optional filename')
args = parser.parse_args()
file1=args.filename1
file2=args.filename2
Then in the cmd you can invoke it as
python script.py -filename="file1.txt"
or
python script.py -filename="file1.txt" --optional="file2.txt"
or
python script.py -filename="file1.txt" --op="file2.txt"
You are looking for argv[1], argv[2], and so on.
This should work:
for filename in sys.argv[1:]:
readfile(filename)

Simple sort program from the command line

I am trying to prevent this simple sort program to accept only 1 argument when printing in reverse. this is what I type and it still goes thru:
python sort.py alpha -r
here is my whole program:
example: python sort.py able spencer delta
to reverse:
python sort.py -r able spencer delta
python sort.py --reverse able spencer delta
import argparse
import sys
def wordsorter():
argparser = argparse.ArgumentParser()
argparser.add_argument('user_string', nargs='*')
argparser.add_argument("-r","--
reverse",action="store_true",default="False",dest="z_to_a")
args = argparser.parse_args()
if (len(sys.argv)==1 or len(sys.argv)==2):
print("Invalid command line arguments to program. Please, supply two or more strings to sort.")
sys.exit(1)
if args.z_to_a == True and len(sys.argv)>1:
words_to_sort= sorted(args.user_string, reverse = True)
print(*words_to_sort)
print("if 1")
else:
words_to_sort = sorted(args.user_string)
print(*words_to_sort)
print("if 2")
if __name__ == '__main__':
wordsorter ()
When I enter one Argument without sorting in reverse it works
If I try to use reverse with one argument it should not work.
What am I doing wrong?
Your problem is caused by the fact that you tell argparse to expect any number of arguments for user_string instead of at least two, and then check that len(sys.argv) >= 2 (by the way, this is a better way to write that if condition), but it can be equal to 2 if you pass -r and only one argument for user_string.
You can check that len(args.user_string) >= 2 and that should work.
If you want to do it through argparse: Unfortunately, while it is possible to tell it to expect at least one argument, it is not possible to directly tell it to expect at least two. A possible workaround is described in this answer.

Unexepected result for 'getopt' function

I have the following function:
def getOptions(logfile):
try:
options, arguments = getopt.getopt(programArguments[1:], 'nt:v:L:d:', ['help'])
except getopt.GetoptError:
print("\nERROR: Invalid Option")
usage()
exit()
where programArguments = sys.argv.
The getopt function is always returning nothing into options and returns a copy of programArguments[1:] into arguments. Where am I going wrong with getopt?
EDIT
See my answer below where I realised my mistake.
Are you using dashes before each argument? (e.g. python program.py -n 1)? If you were to run it like python program.py n 1 it would go into arguments.
You should only see values in arguments if you are passing values that are not assigned to a value. So for this call I get:
> python test_getopt.py --treatment=1
options: [('--treatment', '1')] arguments: []
You would then loop through the (key, value) pairs of options.
On the other hand, if I pass an additional value that is not associated with a key it will be inserted into the arguments list. For example:
> python test_getopt.py --treatment=1 temp.txt
options: [('--treatment', '1')] arguments: ['temp.txt']
See the getopt documentation for more thorough documentation.
I realised that because my program takes a file as argv[1], getopt was exiting once it found that argv[1] was not a valid option. Changing to programArguments[2:] solved the problem.

Error in default programming of Google's Python exercise 'Copyspecial'

I get an IndexError: list index out of range error when using the --todir option in Google's Python Exercise copyspecial.py. How can I resolve this issue? What confuses me the most is that the part of code causing it is what was written by the instructor (from Google/Standford). I can only assume some syntactic error has spilled into other lines of code or that built in function syntax has changed since Python 2.7. This exercise code was written in 2.7.
The file works when no option is used, as so:
Printing list of special files
C:.\gpe\copyspecial\xyz__hello__.txt
C:.\gpe\copyspecial\zz__something__.jpg
done
This is the error:
The code:
def main():
# This basic command line argument parsing code is provided.
# Add code to call your functions below.
# Make a list of command line arguments, omitting the [0] element
# which is the script itself.
args = sys.argv[1:]
if not args:
print "usage: [--todir dir][--tozip zipfile] dir [dir ...]";
sys.exit(1)
# todir and tozip are either set from command line
# or left as the empty string.
# The args array is left just containing the dirs.
todir = ''
if args[0] == '--todir':
todir = args[1]
del args[0:2]
tozip = ''
if args[0] == '--tozip':
tozip = args[1]
del args[0:2]
if len(args) == 0:
print "error: must specify one or more dirs"
sys.exit(1)
# +++your code here+++
# Call your functions
All the aforementioned code is straight from google.com. My code comes before main() is defined and after where it says # +++your code here+++
I have spent hours trying to resolve this. I've learned a lot, but not the solution.
I've tried changing indentations.
I've tried doing sys.exit(1) nest under the '--todir' 'if', but the program keeps running down into the 'if tozip' part, which leads me to believe it's syntactical. But I can't find a misplaced () or :. I also checked indentations.
I've tried adding an 'if args[0]:' check, but it doesn't work, because as I later learned, although an empty list ('args[0]' = []), Python does not interpret it as an actual 'False' value.
The list goes on
I really appreciate the opportunity to have my question heard by the community at stackoverflow, and even more so as a first time poster.
As far as I can see your third try should work if you do it right:
tozip = ''
if args and args[0] == '--tozip':
tozip = args[1]
del args[0:2]
This actually checks the list args. If it is empty ([]), it is considered False and the second test args[0] == '--tozip' does not get evaluated.
Your problem is that args itself is an empty list which does evaluate to False (see https://docs.python.org/2/library/stdtypes.html#truth-value-testing), hence you cannot access args[0] and checking for it results in the same Indexerror.
However, you would still get an IndexError if you only pass one of the parameters without the argument because you access args[1] without testing.
EDIT (Why doesn't the code run as is?): I don't think any python versions >=2.4 would interpret this differently but I have no proof. This argument passing is very basic. Checking for malformed user input is always quite "annoying" because you have to handle every possible input which results in a lot of code. If you want to go into more detail of argument passing I recommend the argparse module (2.7, 3.5). My feeling is that to avoid having a large part of the exercise file that has nothing to do with the exercise they just left it that simple. If you don't supply at least one file path as a parameter you will get an error message in the next step anyway:
if len(args) == 0:
print "error: must specify one or more dirs"
sys.exit(1)
So the code does run as is. You just have to supply the right parameters.

"sys.argv[x]" is out of range

I must run the exact command:
python3 main.py flip pattern.ppm > flippedpattern.ppm
on the following code:
def main(args):
if sys.argv[1] == "flip":
fileName = sys.argv[2]
method = read_ppm(fileName)
print(method.flip())
return
if __name__ == '__main__':
sys.exit(main(sys.argv))
I have a ppm file and I am trying to manipulating it using another module, but it keeps telling me sys.argv[4] is out of range when assigning it to the variable 'outputFile'
The code executed should be essentially this for the flip command
method = "flip"
method = read_ppm("pattern.ppm")
f.write(method.flip())
Doing exactly that using the repl in visual studio leads to success but when I try to run it from the command line through the main module it doesn't work. Why is this? Does the '>' character somehow interfere with sys.argv or am I making a clear counting mistake that I can't see?
The else statement would account for the input not catching and simply printing, using the input
python3 main.py flip pattern.ppm
without any file redirect.
The argument after > is not part of the command line passed to python. It is the file that the shell writes standard output to. So, for example, print "Hello" will write Hello to flippedpattern.ppm.
If you want flippedpattern.ppm to be the fourth command line argument, just leave out the > in the call. (Then, standard output will be written to the screen.)
Edit: Given your modified description, you can write sys.stdout.write(method.flip()). The only difference between this and print(method.flip()) is that print adds a newline character at the end of the string, while .write doesn't.
I'm not sure how Visual Studio works, but if it works with argv[4] it's probably interpreting every word after the script name like another argument (as it's not bash to have a special interpretation for ">").
So for it to work from bash, you should probably change argv[4] in the code to argv[3], and pass the output file as another argument instead of redirecting:
python main.py flip pattern.ppm flippedpattern.ppm
, or leave the code as it is and add a "dummy" third argument so the output file will be the 4th, for example force it to treat ">" as a regular argument as well:
python3 main.py flip pattern.ppm ">" flippedpattern.ppm

Categories

Resources