Are There A Way To Use While Loop Function As A List Tracker? And Mix With Filei/o?
Solution 1:
Fixing your in_listed[0] == 'add'
test
If you want to divide the input string into a list of words, you can use strip
and split
:
in_listed = input('item list: ').strip().split()
Then you can safely test for equality between in_listed[0]
and "add"
, "rm"
, "quit"
.
Read-eval-print loop
What you are implementing is called a "shell" or a "Read-eval-print loop". I suggest simplifying the core of the loop so it reads input, identify a command, and call the appropriate command; the individual code for each command can be separated and put into functions, rather than under an immense if/elif/else
forest inside the loop.
Here is what the loop might look like after simplification:
env = { 'todo_list': [], 'prompt': '$ ', 'didntQuit': True }
command_dict = {'add': cmd_add, 'rm': cmd_rm, 'print': cmd_print_todolist, 'quit': cmd_quit}
while env['didntQuit']:
argv = input(env['prompt']).strip().split() # read
command = command_dict.get(argv[0], cmd_notfound) # identify command
ret = command(argv) # execute command
print_return_value(ret, argv) # error message if command failed
We have a dictionary command_dict
which contains the names of all the commands. In the body of the while loop, we read input; split it into a list of words; look for the first word of the input in the command dictionary; then we evaluate the command found in the dictionary (or cmd_notfound
if we didn't find a command with that name).
Then we can define the functions cmd_add
, cmd_rm
, cmd_print_todolist
and cmd_quit
outside the while loop.
Note that I moved all the global variables to a single dictionary named env
(for "environment") because I am wary of global variable and I prefer to have them all in one place.
Full code
Here is the full code:
env = {
'todo_list': [],
'prompt': '$ ',
'replname': 'repl-todolist',
'didntQuit': True
}
defcmd_add(argv):
iflen(argv) > 2:
date = argv[1]
item = ' '.join(argv[2:])
eliflen(argv) == 2:
date = ''
item = argv[1]
else:
return -1
env['todo_list'].append((date, item))
return0defcmd_rm(argv):
iflen(argv) < 2:
print('rm: please specify the index of the item you want to remove')
return -1try:
i = int(argv[1])
env['todo_list'].pop(i)
return0except ValueError:
print('rm: failed to interpret "{}" as a list index'.format(argv[1]))
return -1except IndexError:
print('rm: index out of range')
return -1defcmd_print_todolist(argv):
print('TODO:')
for i, (date, item) inenumerate(env['todo_list']):
print(' ({}) {} {}'.format(i, date, item))
return0defcmd_quit(argv):
env['didntQuit'] = Falsereturn0defcmd_notfound(argv):
print('{}: {}: command not found'.format(env['replname'], argv[0]))
return -1defprint_return_value(ret, argv):
if ret != 0:
print('{}: command {} failed with return value {}'.format(env['replname'], argv[0], ret))
command_dict = {'add': cmd_add, 'rm': cmd_rm, 'print': cmd_print_todolist, 'quit': cmd_quit}
while env['didntQuit']:
argv = input(env['prompt']).strip().split() # read
ret = command_dict.get(argv[0], cmd_notfound)(argv) # eval
print_return_value(ret, argv) # error message
File I/O
With the code structured this way, it is trivial to add new commands. Just write a new function, and add it to the dictionary of commands!
I suggest you read the tutorial on file I/O in python: Official Doc on Reading and Writing Files.
Here is an example of a "save"
command which would write the todo-list to a file:
defcmd_save(argv):
iflen(argv) < 2:
print('save: please specify a filename to save the todo-list to')
return -1
filename = argv[1]
try:
withopen(filename, 'w') as outf:
for i, (date, item) inenumerate(env['todo_list']):
outf.write('TODO:\n')
for i, (date, item) inenumerate(env['todo_list']):
outf.write(' ({}) {} {}\n'.format(i, date, item))
return0except:
print('save: unable to open file "{}"'.format(argv[1]))
return -1
Then you can add this function to the command dictionary:
command_dict = {'save': cmd_save, 'add': cmd_add, 'rm': cmd_rm, 'print': cmd_print_todolist, 'quit': cmd_quit}
Dates and sorting the todo-list
There is a python module called datetime
which is really cool if you need to parse, manipulate or print dates and times.
I suggest modifying the function cmd_add
by replacing the line date = argv[1]
with something a bit more complex:
defcmd_add(argv):
iflen(argv) > 2:
date = datetime.datetime.strptime(argv[1],"%m/%d").replace(datetime.datetime.today().year)
item = ' '.join(argv[2:])
eliflen(argv) == 2:
date = datetime.datetime.today()
item = argv[1]
else:
return -1
env['todo_list'].append((date, item))
env['todo_list'].sort(key=lambda item: item[0])
return0
Here are the new features:
- Now
date
is no longer a string, but instead adatetime
object; - Since the user doesn't specify a year, I added today's year to the date (see also this answer);
- If the user doesn't specify a date, we add the item with today's date;
- I added
.sort()
at the end of the function. Now your todo-list is sorted by date!
Since the date is a datetime object, we want to modify cmd_print_todolist
as well to print the time under a good format:
defcmd_print_todolist(argv):
print('TODO:')
for i, (date, item) inenumerate(env['todo_list']):
print(' ({}) {} {}'.format(i, date.strftime('%m/%d'), item))
return0
I used '%m/%d'
to format the date, but you could be more fancy and add the day of the week, or write the month in words, etc. See the doc on strptime and strftime behaviour for more info on date parsing and date printing.
Post a Comment for "Are There A Way To Use While Loop Function As A List Tracker? And Mix With Filei/o?"