CSCI 2041 Assignment 3: Multi-List Manager
- Due: 11:59pm Mon 10/22/2018
- Approximately 3.0-4.0% of total grade
- Submit to Canvas
- Projects are individual work: no collaboration with other students is allowed. Seek help from course staff if you get stuck for too long.
CODE DISTRIBUTION: a3-code.zip
TESTING CODE: a3-tests.zip (Instructions)
CHANGELOG:
- Mon Oct 15 11:18:56 CDT 2018
- A bug was identified in the tests due to the presence of the
empty string in some tests. This affects only
test-p3
and can be fixed by updating tests or just replacingtest_bulkops.ml
with this linked version. - Mon Oct 15 10:38:17 CDT 2018
A variety of posts (Post 350, Post 344) have described issues accessing the fields of records defined in other modules, for example the
document
current
field may be needed in other modules likeBulkops.
To avoid the need to use exotic syntax to accomplish this while making the compiler happy, you may useopen
declarations at the tops of modules to make the compiler aware of the record types in those modules. Continue to use the module names in function calls to help readers understand the context such asDocument.set
andBulkops.add_all
. Manual inspection criteria have been modified on relevant problems to reflect this change.As discussed in Post 349,
execute_command
should honor theload <file>
command which reads the contents of a file and replaces the current list with them, an undoable action. There is a caseload
in the provided code but regrettably it was omitted from the provided help string.- Sun Oct 14 23:41:27 CDT 2018
- Automated tests have been added to the assignment. Download code at the top of the spec and follow the associated instructions.
- Fri Oct 12 13:01:04 CDT 2018
- The documentation strings in the spec for
bulkops.ml
have been corrected. Thebulkops.ml
functions all take aDoccol.doccol
argument. Previous versions of the spec mentioned a non-existentDocument.doclist
type from a draft of the project. This typo was noticed in Post 342. The mention ofdoc_col
types has also been corrected todoccol
. - Thu Oct 11 12:01:48 CDT 2018
- Addition of "no printing" grading criteria to
document.ml
anddoccol.ml
. Error messages should be printed inmultimanager.ml
based on true/false return values from functions working with documents and collections. - Wed Oct 10 08:59:23 CDT 2018
- Typos/clarifications on
doccol
: thehas
function takes an additional string argument which was omitted in the documentation string; use of pattern matching indoccol.ml
will be necessary to handle theoption
's that must be used. - Tue Oct 9 10:53:55 CDT 2018
- Minor typo fix for documentation of
Doccol.switch
function.
1 Overview
A major limitation of listmanager
was its focus on a single
list. This assignment upgrades to multimanager
which adds the
following features.
- Multiple lists handled independently each with its own undo/redo history
- Switching between lists and conveniences like saving and loading based on list name / file name correspondences
- Bulk operations to add to all lists and merge them
The design makes extensive use of the following OCaml features. Acquaint yourself with them to ensure success.
- Records with mutable fields
- Association lists to track key/value pairs (named "documents")
- Options to work with searching when it is not certain a key will be found
- Higher-order functions to apply functions to all elements of a list
1.1 Assignment Files and Download
Download the code pack linked at the top of the page. Unzip this which will create a project folder. Create new files in this folder. Ultimately you will re-zip this folder to submit it.
NOTE: Tests Released Later: The tests for this assignment will not be released with the publication of the specification. They will be released sometime after once they have been completed. Further instructions on how to use them will be made available on release.
File | State | Prob# | Notes |
---|---|---|---|
Makefile |
Provided | All | Build file to compile all programs |
mltest.ml |
Provided | All | Testing utilities/main routine |
process-mltest.awk |
Provided | All | Awk script to add debug info to test files |
util.ml |
Provided | 4 | Utility functions for file I/O. |
sortedlist.ml |
Copy in | - | Ops on sorted lists, identical to A2 file |
document.ml |
Create | 1 | Data type with current state and undo/redo history |
test_document.ml |
Provided | 1 | Unit Tests for Problem 4 |
doccol.ml |
Create | 2 | Data type for collection of unique named documents with a current doc tracked |
test_doccol.ml |
Provided | 2 | Unit Tests for Problem 2 |
bulkops.ml |
Create | 3 | Functions for operations on all elements of a doccol |
test_bulkops.ml |
Provided | 3 | Unit Tests for Problem 3 |
multimanager.ml |
Modify | 4 | Main program loop, modify to fill in missing code |
test_multimanager.sh |
Provided | 4 | Application Tests for Problem 4 |
test_multimanager_data.sh |
Provided | 4 | Data for application tests |
Automated Testing Instructions
Unzip a3-tests.zip
to create the directory a3-tests/
. Copy all
files in the a3-tests
directory to the project directory
a3-code
. The following unix command should do this if a3-tests
and
a3-code are in the same directory.
cp -r a3-tests/* a3-code/
The Makefile provided is a copy of Makefile.withtests. The original version is called Makefile.notests.
The new Makefile introduces the targets
make test-p1
: test problem 1make test-p2
: test problem 2make test-p3
: test problem 3make test-p4
: test problem 4make test
: test all problems
Test programs test_document
, test_doccol
, and test_bulkops
associated with problems 1-3 can run individual tests by passing a
test number as a command line argument as in
./test_doccol 3 # run the 3rd test only
Test program test_multimanager.sh
associated with problem 4 also has
this feature
./test_multimanager.sh 10 # run only the 10th test
1.2 General Grading Criteria grading
Weight | Criteria |
---|---|
5 | CORRECT SUBMISSION and STYLE |
All files submitted and properly zipped | |
make test compiles all code and runs tests |
|
Code is indented reasonably well to indicate scopes | |
Comments provide insight for intricate blocks |
1.3 Demo Session
multimanager
is demonstrated below. Study this demo session to gain
an understanding of the direction of the program.
> make # compile ocamlc -g -annot -c util.ml ocamlc -g -annot -c sortedlist.ml ocamlc -g -annot -c document.ml ocamlc -g -annot -c doccol.ml ocamlc -g -annot -c bulkops.ml ocamlc -g -annot -c multimanager.ml ocamlc -g -annot -o multimanager str.cma unix.cma util.cmo sortedlist.cmo document.cmo doccol.cmo bulkops.cmo multimanager.cmo > ./multimanager # run (default.txt)> help # show help MULTI MANAGER Maintains multiple sorted lists of unique elements.. --PROGRAM COMMANDS--: help : print this help message quit : quit the program --CURRENT LIST COMMANDS-- The following commands modify the current list show : print the current list to the screen clear : set the list to empty, preserves undo history add <elem> : add elem to the list remove <elem> : remove elem from list mergein <file> : load the sorted list in the named file and merge with current list (undoable) save : save the current list using the name of the list as the save file saveas <file> : save the current list to the given file name; keeps the list name the same undo : undo the last operation restoring list to a previous state redo : redo the last undone operation restoring list to a previous state --LIST MANAGEMENT COMMANDS-- The following commands will fail if a list name is already in use (new/open) or no present (close/edit/merge) lists : prints the lists that are currently open edit <list> : set the named list to the current list new <list> : create a new empty list and switch to it open <file> : create a new list named after the file specified; load the contents of the file into it and switch to it close <list> : close the list with given name and remove it from the open documents; cannot close the current list merge <list> : merge the named list contents into the current list --BULK OPERATIONS-- The following commands act upon all open lists showall : print all lists labelled with their list name saveall : save all open lists; use filenames identical the list names (not undoable) addall <elem> : add elem to all open lists; each list can undo this individually mergeall : merge the contents of all lists into the current list; undoable (default.txt)> add Korra # add some elements to default.txt (default.txt)> add Bolin (default.txt)> add Mako (default.txt)> show # show current list --BEG LIST-- Bolin Korra Mako --END LIST-- (default.txt)> new others.txt # create a new list others.txt and switch to it (others.txt)> show # others.txt is empty --BEG LIST-- --END LIST-- (others.txt)> add Tenzin # add some element to it (others.txt)> add Pema (others.txt)> show # show --BEG LIST-- Pema Tenzin --END LIST-- (others.txt)> lists # show the open lists 2 docs - others.txt - default.txt (others.txt)> edit default.txt # switch back to default.txt (default.txt)> show --BEG LIST-- Bolin Korra Mako --END LIST-- (default.txt)> undo # undo history is retain independently for each list (default.txt)> show --BEG LIST-- Bolin Korra --END LIST-- (default.txt)> add Asami (default.txt)> redo # last op was an add so no redo history WARNING: redo list empty, no changes made (default.txt)> show --BEG LIST-- Asami Bolin Korra --END LIST-- (default.txt)> new vils.txt # make new list vils.txt and switch to it (vils.txt)> add Unalaq (vils.txt)> add Kuvira (vils.txt)> show --BEG LIST-- Kuvira Unalaq --END LIST-- (vils.txt)> close vils.txt # cannot close the current list ERROR: cannot close the current list (vils.txt)> edit others.txt # switch back to others.txt (others.txt)> show --BEG LIST-- Pema Tenzin --END LIST-- (others.txt)> save # save list to file after its name "others.txt" (others.txt)> new tmp # make a new list called tmp (tmp)> load others.txt # load file into tmp (tmp)> show # show contents that were saved from another list --BEG LIST-- Pema Tenzin --END LIST-- (tmp)> edit default.txt # switch to list default.txt (default.txt)> close tmp # close list tmp discarding it (default.txt)> edit tmp # cannot edit this as it was just closed ERROR: list 'tmp' does not exist (default.txt)> lists # show open lists 3 docs - vils.txt - others.txt - default.txt (default.txt)> showall # show all contents of all lists --List vils.txt-- Kuvira Unalaq --List others.txt-- Pema Tenzin --List default.txt-- Asami Bolin Korra (default.txt)> merge others.txt # merge list others.txt into current list default.tt (default.txt)> show # now has contents of other list --BEG LIST-- Asami Bolin Korra Pema Tenzin --END LIST-- (default.txt)> undo # merge can be undone (default.txt)> show --BEG LIST-- Asami Bolin Korra --END LIST-- (default.txt)> redo # redo the merge (default.txt)> show --BEG LIST-- Asami Bolin Korra Pema Tenzin --END LIST-- (default.txt)> saveall # save all open files to their associated file names (default.txt)> lists # show open lists 3 docs - vils.txt - others.txt - default.txt (default.txt)> new tmp # make a new tmp list (tmp)> load vils.txt # load contents of file vils.txt (tmp)> show # was saved as part of earlier saveall --BEG LIST-- Kuvira Unalaq --END LIST-- (tmp)> edit default.txt # switch to default.txt (default.txt)> close tmp # close tmp lsit (default.txt)> open test-data/heros.txt # open file create a list named after it (test-data/heros.txt)> show --BEG LIST-- Asami Bolin Bumi Jinora Korra Kya Mako Tenzin --END LIST-- (test-data/heros.txt)> undo # initially opened files have no undo history WARNING: undo list empty, no changes made (test-data/heros.txt)> lists # show open lists 4 docs - test-data/heros.txt - vils.txt - others.txt - default.txt (test-data/heros.txt)> showall # show all data in all lists, some is redundant --List test-data/heros.txt-- Asami Bolin Bumi Jinora Korra Kya Mako Tenzin --List vils.txt-- Kuvira Unalaq --List others.txt-- Pema Tenzin --List default.txt-- Asami Bolin Korra Pema Tenzin (test-data/heros.txt)> addall Meelo # add Meelo to all lists (test-data/heros.txt)> showall # show addition succeeded --List test-data/heros.txt-- Asami Bolin Bumi Jinora Korra Kya Mako Meelo # Meelo added Tenzin --List vils.txt-- Kuvira Meelo # Meelo added Unalaq --List others.txt-- Meelo # Meelo added Pema Tenzin --List default.txt-- Asami Bolin Korra Meelo # Meelo added Pema Tenzin (test-data/heros.txt)> new everyone # make a new empty list (everyone)> show --BEG LIST-- --END LIST-- (everyone)> mergeall # merge all lists into the current list (everyone)> show # show resulting merged list, no duplicates --BEG LIST-- Asami Bolin Bumi Jinora Korra Kuvira Kya Mako Meelo Pema Tenzin Unalaq --END LIST-- (everyone)> quit # exit the program Lists multi-managed!
2 Baseline: Sortedlist
Like listmanager
, multimanager
maintains lists in sorted order.
To that end sortedlist.ml
can be used in without modification. For
completion, the top-level definitions expected in sortedlist.ml
are
shown below and are identical to those from the last assignment.
(* sortedlist.ml : Provides operations for sorted lists of any type. Identical functionality to A2 sortedlist.ml. *) let rec insert list elem = ... ;; (* val insert : 'a list -> 'a -> 'a list Insert elem into list which is sorted. The insertion preserves the sorted order of the list. No duplicates are allowed in the list: if elem is equal to an element of list, the resulting list is identical to the original list. Uses pattern matching, not tail recursive. Runs in linear time on length of list. *) let rec remove list elem = ... ;; (* val remove : 'a list -> 'a -> 'a list Create a new list with elem removed from the parameter list. If elem is not present in list, the result is identical to the original list. Uses pattern matching, not tail recursive. Runs in linear time on length of list. *) let rec print strlist = ... ;; (* val print_strlist : string list -> unit Print all elements of a string list to standard output. Makes use of standard printing functions such as print_endline or printf to print. Uses pattern matching. This function IS tail recursive. Runs in linear time on length of list. *) let rec merge lista listb = ... ;; (* val merge : 'a list -> 'a list -> 'a list Merge two sorted lists: lista and listb. Elemetns that appear in both lists appear only once in the result. Operates in linear time on the length of lists: does not do repeated insertion. Not tail recursive. May use pattern matching OR if/else clauses. Runs in linear time on combined length of lists. *)
3 Problem 1: The Document Type
The document
type modularizes the functionality of undo/redo for an
individual entity. This enables independent states and undo/redo
individually. It is a record comprised of 3 mutable fields.
current
tracks the current state of the document much ascurr_list
did inundolist.ml
undo_stack
andredo_stack
which serve the same purpose as they did inundolist.ml
Associated with the type are several functions that operate that allow
setting the document
to a new data and undo and redo those sets.
In the context of multimanager
, all documents will be string list
documents
but the type specification is 'a document
which means any
kind of document can be tracked such as int docuemnt
or boolean
array document
which are used in testing.
3.1 document.ml
Top-Level Definitions
Create a source file document.ml
which will have the following
bindings in it.
(* document.ml: Defines a "document" type which tracks a current state along with undo/redo history. The document type is polymorphic meaning data can be any type such as 'int document' or 'string list document'. *) (* document type declaration. *) type 'a document = { mutable current : 'a ; (* current document state *) mutable undo_stack : 'a list; (* past states that can be reached via undo *) mutable redo_stack : 'a list; (* undone states that can be reached via redo *) };; let make initial = ... ;; (* val make : 'a -> 'a document Create a new document with initial as the current state and empty undo/redo stacks. *) let set document data = ... ;; (* val set : 'a document -> 'a -> unit Set the document to the given data. Push current state into the undo stack. Empty the redo stack *) let undo document = ... ;; (* val undo : 'a document -> bool If the undo_stack is not empty, undo the last operation. current is moved to the redo_stack and the top element of undo_stack is removed and becomes current. Returns true if changes are made and false if undo_stack is empty and no changes are made. Operates in constant time. *) let redo document = ... ;; (* val redo : 'a document -> bool If the redo_stack is not empty, redo the last operation. current is moved to the undo_stack and the top element of redo_stack is removed and becomes current. Returns true if changes are made and false if redo_stack is empty and no changes are made. Operates in constant time. *)
3.2 Implementing document
Functions
- These functions are close in functionality to those of
undolist.ml
from the prior assignment with the major change being that they each operate on a parameterdocument
rather than global variables. - Make use of the dot notation to access fields of the record such as
in
document.current
anddocument.undo_stack
Since
document
fields are all declaredmutable
, they may be re-assigned to new values using the left-arrow syntax as indocument.current <- something new;
- Otherwise, the semantics of set/undo/redo are identical to the previous assignment and should be familiar. If not, refer back to this section on their implementation
3.3 Grading Criteria for document.ml
grading
The following criteria will be checked in this problem.
Weight | Criteria |
---|---|
AUTOMATED TESTS for document.ml |
|
10 | make test-p1 correctly compiles and passes tests |
MANUAL INSPECTION for document.ml |
|
10 | set / undo / redo |
Each works on individual document records, not global references |
|
Proper use of dot syntax to access fields and <- to mutate fields |
|
Use of pattern matching is optional and not required | |
No output is printed: only true/false values are returned to indicate success/failure |
4 Problem 2: Document Collections
multimanger
handles multiple documents via functionality provided in
the doccol.ml
source file. It defines a doccol
type which
tracks multiple documents in a standard association list. Recall that
an association list is simply a list of key-value pairs. In this
setting the keys are the names of the documents like default.txt
or
test-data/heros.txt
. doccol
functions ensure that the document
names are kept unique, an added constraint over standard association
lists. The add/remove functions mention how to handle
missing/redundant document names.
doccol
also maintains a Current Document and its name. This
document cannot be removed as stated in the documentation of
remove
. This ensures that there is always at least one document in
the collection at all times.
Importantly, no exception handling is needed for functions in
doccol.ml
as options
are used: failure to find a named document
should return None
if appropriate functions are employed such as
List.assoc_opt
.
4.1 doccol.ml
Top-Level Definitions
Create a source file doccol.ml
which will have the following
bindings in it.
(* doccol.ml: Type and functions for a collection of named documents. Tracks a current document and its name along with an association list of all docs in the collection. Preserves uniqueness of names in the collection. Makes use of built-in List functions to ad/remove/get docs from the association list. *) (* Type to track a collection of named documents in an association list. *) type 'a doccol = { mutable count : int; (* count of docs in list *) mutable curdoc : 'a Document.document; (* current list being edited *) mutable curname : string; (* name of current list *) mutable docs : (string * 'a Document.document) list; (* association list of names/docs *) };; let make name doc = ... ;; (* val make : string -> 'a Document.document -> 'a doccol Create a doccol. The parameters name and doc become the current doc and the only pair in the docs association list. *) let add doccol name doc = ... ;; (* val add : 'a doccol -> string -> 'a Document.document -> bool If there is already a doc with name in doccol, do nothing and return false. Otherwise, add the given doc to doccol with the given name, update the count of docs and return true. Uses association list functions from the List module. *) let remove doccol name = ... ;; (* val remove : 'a doccol -> string -> bool If name is equal to curname for the doccol, do nothing and return false. If there is no doc with name in doccol, do nothing and return false. Otherwise, remove the named doc from doccol, decrement the count of docs, and return true. Uses association list functions from the List module. *) let has doccol name = ... ;; (* val has : 'a doccol -> string -> bool Returns true if the named doc is in the doccol and false otherwise. *) let switch doccol name = ... ;; (* val switch : 'a doccol -> string -> bool Change the current document/name to the named document and return true. If the named document does not exist, return false and make no changes to doccol. *) let string_of_doccol doccol = ... ;; (* val string_of_col : 'a doccol -> string Creates a string representation of doccol showing the count of docs and the names of all docs. Each doc is listed on its own line. It has the following format: 4 docs - test-dir/heros.txt - places.txt - stuff.txt - default.txt Does not define any helper functions. Makes use of higher order functions such as List.map and/or List.fold. May also use string processing functions such asString.concat and/or Printf.sprintf *)
4.2 Implementing doccol
Functions
- Make use of functions that exist in the
List
module, notableassoc_opt
will be useful in several situations. - Since
assoc_opt
will returnoption
's, you will need to utilize pattern matching in many of thedoccol
functions. - Do Not make use of any exception handling. Use this as an
opportunity to gain some familiarity with
options
- Notice that
add
ensures that document names remain unique. This means searching the collection for a name to ensure it does not already exist. If not, add it and returntrue
to indicate success. If not, make no change and returnfalse
. - Similarly, removing a document that does not exist in the collection
does not change anything about the collection and returns
false
to indicate no change was made. - The final function
string_of_docs
should employ a higher-orderList
function such asmap / fold / iter
to produce the resulting string with little code effort. String.concat
andPrintf.sprintf
are also useful for this function in conjunction with higher-order list functions. Review descriptions associated with these in the OCaml Standard library documentation if they are unfamiliar.
4.3 Grading Criteria for doccol.ml
grading
The following criteria will be checked in this problem.
Weight | Criteria |
---|---|
AUTOMATED TESTS for doccol.ml |
|
10 | make test-p2 correctly compiles and passes tests |
MANUAL INSPECTION for doccol.ml |
|
You may use the open directive to open modules to ensure all record fields are known to the compiler |
|
5 | add / remove / get / has / switch |
Makes use of options and pattern-matching on options if needed |
|
No use of exception handling | |
Appears to maintain uniqueness of doc names (keys) and returns appopriate booleans | |
Makes use of mutable record access via dot and mutation via <- |
|
remove has a clear case preventing removal of the current document which returns false |
|
No output is printed: only true/false values are returned to indicate success/failure | |
5 | string_of_doccol |
Makes use of higher-order List function(s) to produce string |
|
Employs String.concat and/or Printf.sprintf to produce the final string |
|
No use of iteration/recursion or helpers to accomplish the stringification | |
Relatively short code through use of higher-order functions |
5 Problem 3: Bulk Operations
multimanager
allows "bulk operations" that operate on all open lists
(documents). This allows a single user command save or show all lists
or add an element to all lists.
These operations do not apply to any 'a doccol
but rather are
specialized to only work for string list doccol
as they will make
use of Sortedlist
functions. For that reason, they are placed in a
separate source file, bulkops.ml
rather than in doccol.ml
.
Most of these functions are short and involve two parts:
- Definition of a helper function that operates on a single
name /
document
pair. - Use of higher-order
List
function(s) to apply the helper to all of the name/docs in the givendoccol
collection.
5.1 bulkops.ml
Top-Level Definitions
Create a source file bulkops.ml
which will have the following
bindings in it.
(* bulkops.ml: Implement bulk operations on Doccol's of string list Documents that are useful for multimanager. Since the functions in this module require access to fields and types of other modules, start the file by opening those two modules: open Document;; open Doccol;; *) let showall doccol = ... ;; (* val showall : string list Doccol.doccol -> unit Prints all documents in doccol to the screen. For each list, prints the list name first and then each element of the list using Sortedlist functions. Uses higher-order functions to iterate over the doclist. EXAMPLE: --List test-data/heros.txt-- Asami Bolin Bumi Jinora Korra Kya Mako Tenzin --List test-data/villains.txt-- Amon Hiroshi Kuvira Ming-Hua P-li Unalaq Zaheer --List default.txt-- Korra Meelo Pema *) let saveall doccol = ... ;; (* val saveall : string list Doccol.doccol -> unit Saves all documents in doccol. Makes use of Util functions to do I/O. Makes use of higher-order functions to write each list to associated file name. *) let addall doccol elem = ... ;; (* val addall : 'a list Doccol.doccol -> 'a -> unit Adds the given element to all docs in doccol. Makes use of higher-order functions and Sortedlist functions to modify each list. Each doc/list can individually undo the addition. *) let mergeall doccol = ... ;; (* val mergeall : 'a list Doccol.doccol -> 'a list Merges all lists in doccol.docs into a single list and returns it. Uses higher-order functions and Sortedlist functions to perform the merge. *)
5.2 Implementing bulkops.ml
Functions
- Each of these functions takes a
doccol
as an argument and works with the fielddoccol.docs
which is an association list. - As suggested above all of these functions define helper functions
and then make use of higher-order
List
functions to apply them to alldoccol.docs
elements - Make sure each helper is nested within the scope of the relevant function: no new module-level bindings should be introduced
Helper functions will be applied to the elements of association list
doccol.docs
which is comprised of name/doc pairs. It is therefore helpful to define helpers as taking a pair argument with named parts such aslet master_func doccol = let helper (name,doc) = ... (* use name and doc *) in ... ;;
- Helper functions are not required to be named and can instead be
anonymous using the
fun
syntax though this is optional. - Make use of an appropriate higher-order functions from
List
to apply the helper function to all elements of the association list. Consider the following when selecting an appropriate higher-order function.- Changes to each element that result in
unit
are often associated withList.iter
- Extracting some elements from the list is often associated with
List.filter
. The resulting list will likely be shorter but have the same type of elements. - Mapping a function to all elements in the list to produce a new
list the business of
List.map
. The resulting list will have the same length as the input list but may have a different type of element. - Creating a new result that combines all elements of a list is
usually done with
List.fold_left
orList.fold_right
.
- Changes to each element that result in
5.3 Grading Criteria for bulkops.ml
grading
The following criteria will be checked in this problem.
Weight | Criteria |
---|---|
AUTOMATED TESTS for bulkops.ml |
|
10 | make test-p3 correctly compiles and passes tests |
MANUAL INSPECTION for bulkops.ml |
|
You may use the open directive to open modules to ensure all record fields are known to the compiler |
|
10 | showall / saveall / addall / mergeall |
Defines a clear helper function that operates on a single name/doc pair | |
Helper function can be named or an anonymous fun |
|
Uses an appropriate higher-order List function to apply helper to all docs |
|
addall mutates all lists (docs) |
|
mergeall does not change any lists but produces a resulting merged list |
|
No use of iteration/recursion in favor of pre-defined higher-order List functions |
6 Problem 4: The Multimanager
As with the previous assignment, code in multimanager.ml
glues
together the different modules into a working program. Also as
before, most of the action occurs in the execute_command
function which is called in a provided main loop near the bottom of
the provided multimanager.ml
.
Due to the large number of commands to be implemented, the overall
structure of the central match/with
in execute_command
is provided
but most of the cases in the provided code simply throw exceptions.
This scaffolding will help to ensure that no commands are missed and
that graders can expect a specific order of viewing for the
commands. Fill in code for these commands according to the new
architecture.
multimanager
binds the name global
to a doccol
record. This
enables it to track two pieces of state described below.
- Current List: the list that commands like
show
andadd elem
will work with. Alternatively referred to as the current document as it is adocument
record. The name of the current list is shown on the prompt. - Open List Collection: the multiple lists that are currently stored in memory by the program. The Current List is in the Open Lists Collection. The Current Document can be changed to a different member of the Open List Collection. The collection can increase and decrease in size.
let global : doccol = ...;;
Thus global.curdoc
refers to the list (document) that is currently
the focus of the application while global.docs
is the Collection of
other lists being edited. Most cases in execute_command
will make
changes to fields of global
such as the following examples:
add <elem>
inserts an element intoglobal.curdoc
open <file>
modifiesglobal
to populatedocs
with the contents of a file and changecurdoc
to itedit <list>
searches for a list inglobal.docs
and changesglobal.curdoc, global.curname
, all using functions fromdoccol.ml
.
Most of the commands dealing with changes to the Collection or Current
List should use Doccol
functions to make the changes.
6.1 multimanager.ml
Top-Level Definitions
The source file multimanager.ml
is provided and has the following
structure. Edit the execute_command
function so that it implements
the indicated commands.
(* multimanager.ml : main function to allow manipulation of multiple lists of sorted, unique elements. *) let help_string = ... ;; (* Help string to be printed for the "help" command. *) let global : string list Doccol.doccol = ... ;; (* Tracks the global state associated with the application. This binding uses a series of statements to initialize the global state to have a default empty list named 'default.txt' which is the current document and the only entry in the doccol. *) let quit_now = ... ;; (* Set to true to end execution of the program *) let execute_command tokens = ... ;; (* val execute_command : string array -> unit Execute a single command which is the 0th element of the argument array tokens. If the command has additional parameters these will be in tokens.(1), tokens.(2), etc. Makes use of functions in Util, Sortedlist, Document, doccol, and Bulkops to implement each command. *) let echo = ... ;; (********************************************************************************* Code beyond this point should not require modification though it may be interesting to examine. *) let debug = ... ;; let options = ... ;; (* Options accepted by the program *) let handle_extra_args arg = ... ;; (* Do nothing with extra command line arguments *) let usage = ... ;; (* Simple usage message for Arg.parse *) let _ = ... ;; (* main routine *)
6.2 Implementing execute_command
- DO NOT MODIFY THE ORDER of commands in
execute_command
. Graders will expect code to appear in the order given and altering this will irritate them. Whenever possible, avoid irritating graders. - Do not open any modules other than
Printf
which is already done. Instead, refer to functions in various source files via the module syntax discussed in the last assignment. - The below subsections give some details associated with the major types of commands and how they should be implemented.
6.3 Current List Commands
You may wish to consult your solution for
listmanager.ml
for theCURRENT LIST COMMANDS
but note that you will need to make some substantial changes due to move from a single global undo list to use of individualdocument
records with undo/redo.The other commands for list management and bulk operations are new enough to need from-scratch implementation.
- Keep the access pattern to the Current List in mind
global.curdoc
is astring list document
which means functions fromDocument
such asset
andundo
are applicable to it.global.curdoc.current
is astring list
so functions fromSortedlist
such asinsert
are applicable to it.
- Inspect the provided
show
andclear
commands as examples of how this works. These are similar to equivalents inlistmanager
but work with the individualglobal.curdoc
rather than the a global undo list. - Note the slight change in the
save
command: it no longer takes a file argument but usesglobal.curname
as the file to save into. Commandsaveas
takes an additional argument intokens.(1)
as the file name to save.
6.4 List Management Commands
- Each of these commands take a maximum of around 10 lines of code to complete. If you implementation badly exceeds that, rethink your solution path.
- Most commands manipulate
global
using functions fromDoccol
such asDoccol.add
andDoccol.switch
. edit <list>
: If the names list exists, it becomes the Current List. If not, print a warning message as follows:printf "ERROR: list '%s' does not exist\n" name;
This command should make use of options and never raise exceptions.
new <list>
andopen <file>
: Check that the resulting named list is not already part of the Open List Collection. If it is, print an error message as followsprintf "ERROR: list '%s' already exists\n" name
Otherwise,
new
switches to a new empty list whileopen <file>
reads the file contents as the initial list usingUtil
functions. Both create documents soDocument.make
should be employed.close <list>
: remove the named list from the Open List Collection. If<list>
is the current list, do not remove anything and print an error message with the viaprintf "ERROR: cannot close the current list\n"
If
<list>
is not in the Open List Collection print the error messageprintf "ERROR: list '%s' does not exist\n" name;
There may be a few situations in which one wishes to call
switch
simply for its side-effect of switching the current document. Make use of theignore
syntax to do so without generating a compiler warning as inignore(Doccol.switch ...);
This will cause the returned true/false value to be ignored so the compiler does not complain that the `expression should have type unit'.
merge <list>
: Similar toclose
, if the names list does not exist print an error message:printf "ERROR: list '%s' does not exist\n" name;
Otherwise use
Sortedist
functions to merge the named list into the Current List.
6.5 Bulk Operations
- Each of these commands has very short implementations that draw on
functions in
Bulkops
showall / saveall / addall
are essentially calls to correspondingBulkops
functionsmergeall
requires that the Current List be changed; after creating the merged list, useDocument.set
for this so that the operation can be undone.
6.6 Grading Criteria for execute_command
grading
The following criteria will be checked in this problem.
Weight | Criteria |
---|---|
AUTOMATED TESTS for execute_command in multimanager.ml |
|
15 | make test-p3 correctly compiles and passes tests |
MANUAL INSPECTION for execute_command in multimanager.ml |
|
You may use the open directive to open modules to ensure all record fields are known to the compiler |
|
5 | CURRENT LIST COMMANDS |
Makes use of appropriate functions in Util, Sortedlist, Document . |
|
Short implementations due to use of existing functions. | |
Use of Document.set in most commands to enable undo/redo capabilities. |
|
If not able to undo/redo, error messages are printed in multimanager.ml |
|
5 | LIST MANAGEMENT COMMANDS |
Makes use of appropriate functions in Doccol |
|
Short implementations due to use of existing functions. | |
If not able to edit a list, error messages are printed in multimanager.ml |
|
5 | BULK OPERATIONS |
Makes use of appropriate functions in Bulkops |
|
Short implementations due to use of existing functions. | |
5 | GENERAL CRITERIA for execute_command |
Preserved existing order of commands in match statement |
|
No modification to signature for execute_command : takes string array , returns a bool |
|
Some comments which indicate how functions are being used to implement commands | |
Make use of module function access syntax like Sortedlist.add and Doccol.switch |
7 Zip and Submit
7.1 Zipping Files
If creating a zip file is unfamiliar to you, refer to the following guide: Making Zips (tutorial)
7.2 Submit to Canvas
Once your are confident your code is working, you are ready to submit. Ensure your folder has all of the required files. Create a zip archive of your lab folder and submit it to Canvas.
On Canvas:
- Click on the Assignments section
- Click on the appropriate link for this lab/assignment
- Scroll down to "Attach a File"
- Click "Browse My Computer"
- Select you Zip file and press OK
7.3 Late Policies
You may wish to review the policy on late project submission which will cost you late tokens to submit late or credit if you run out of tokens. No projects will be accepted more than 48 hours after the deadline.
http://www-users.cs.umn.edu/~kauffman/2041/syllabus.html#late-projects