Last Updated: 2018-09-14 Fri 16:51

CSCI 2041 Assignment 1: OCaml Basics

CODE DISTRIBUTION: a1-code.zip

CHANGELOG:

Thu Sep 13 10:30:14 CDT 2018
The grading criteria for each problem has been updated to remove some erroneous testing targets like make test-abovefuncs. The proper targets are make test-p1, make test-p2, make test-p3.

Problem 2 listed the return type of array_above as unit incorrectly. This has been fixed to match other parts of the spec where it is 'a array.

Mon Sep 10 19:26:34 CDT 2018
Post 29 correctly identified a typo in the implementation notes for array_above. The correct invocation is Array.make length x.
Mon Sep 10 10:36:05 CDT 2018
On some platforms, the process-mltest.awk script which is provided may not be executable. This will manifest itself as tests seemingly having no results. This can be solved by running the following command once in a terminal in the a1-code directory:
> chmod u+x ./process-mltest.awk

Alternatively, download a new copy of the assignment Makefile which will do this for you. The code pack has been updated so that the new version of the Makefile is present now.

Post 22 identified a version a problem on CSELabs machines which causes failures compiling tests. This is remedied by using a more recent version of OCaml than the default: load it on CSELabs machines using

> module load soft/ocaml/4.06.0 

1 Rationale

OCaml is an expressive programming language with a somewhat unusual syntax with respect to more mainstream languages such as Java, C, and Python. This assignment explores expressing basic computations such as name bindings, conditionals, function definition, lists, arrays, and iteration in OCaml. Completing the assignment will demonstrate the ability to write basic programs from other languages in the OCaml. It will also prepare one to understand more advanced programming mechanisms available in OCaml that are not present in main stream languages.

2 Overview

The assignment is divided into several short problems described in subsequent sections. Each problem is fairly independent from the others so may be worked on in any order.

2.1 Grading Criteria

Credit for this assignment will be given based on two categories.

Manual Inspection Criteria

Each problem has a checklist of things that graders will look for. The checklist is in the spec and often contains hints on what to do. Make sure you have a look at these.

Automated Testing

Most code on assignments will have associated automated tests. These are either available with the assignment code or downloaded separately. Instructions on how to use these will be published in the assignment specification so that you can run the tests yourself to see how many points you are getting. Tests require that code compiles and runs according to the descriptions given so make sure you verify that these work.

2.2 Getting Started

Take the following steps to get started

  1. Download the code associated with the project linked at the top of the spec. Unzip it and examine some of the provided code.
  2. Examine the overview of the files provided listed in the Download and Setup section. This gives brief descriptions of files that already exist and those that you must create.
  3. Pick a problem and read. There is a lot of information and many examples provided for each problem. Reading this will help you write the correct code earlier rather than later.
  4. Ask questions: if its not clear how to proceed, put up a Piazza post or visit an office hour.
  5. Get coding: don't wait to start for too long as this will greatly increase your stress levels an potentially result in late submissions.
  6. Familiarize yourself with the late submission policy for assignments so you are not caught off guard. No submissions will be accepted more than 48 hours after the deadline.

2.3 Makefile and Automated Tests

A Makefile is provided as part of this assignment. Building programs in any language is a bit tedious and most folks use build systems of which make is the oldest. The instructions and dependencies to create programs are written in a Makefile which is then interpreted by the make program which will run the OCaml compiler and other commands to create programs.

  • make sumfuncs.cmo will compile the code for problem 1
  • make test-p1 will compile a test program and run it for problem 1
  • ./test_sumfuncs is the test program for problem 1. It runs all tests be default but typing ./test_sumfuncs 3 will run only test #3
  • make clean will remove all compiled files and programs
  • make on its own will compile all required files and programs
  • make test will compile everything and run all tests.

Tests are known to work on lab machines only. They may work on other machines/environments but this is not guaranteed or supported. Check that your code passes tests on lab machines before submitting.

Detailed usage and output from using the provided Makefile are shown below.

> make sumfuncs.cmo                  # build code for problem 1
ocamlc -g -annot -c sumfuncs.ml      # make runs command command

> make sumfuncs.cmo                  # build again
make: 'sumfuncs.cmo' is up to date.  # no need as no changes have been made

> make abovefuncs.cmo                # build code for problem 2
ocamlc -g -annot -c abovefuncs.ml    # make runs compile commands

> make clean                         # remove all compiled/auxiliary files
rm -f *.cmo *.cmi *.mlt *.annot collatz_main  test_sumfuncs test_abovefuncs test_revfuncs test_collatz 

> make test-p1                       # compile and run tests for problem 1
ocamlc -g -annot -c mltest.ml        # compile various stuff to create test_sumfuncs program
ocamlc -g -annot -c sumfuncs.ml
./process-mltest.awk test_sumfuncs.ml > test_sumfuncs.mlt
ocamlc -g -annot -c test_sumfuncs.mlt
ocamlc -g -annot -o test_sumfuncs mltest.cmo sumfuncs.cmo test_sumfuncs.cmo
===TESTS for P1 sumfuncs.cmo===
./test_sumfuncs                      # run test_sumfuncs program
Found 10 tests
RUNNING 10 tests
==================================================
Test  1: ok
Test  2: ok
Test  3: ok
Test  4: ok
Test  5: ok
Test  6: ok
Test  7: ok
Test  8: ok
Test  9: ok
Test 10: ok
==================================================
10 / 10 tests passed                 # looks good


> make test-p2                       # compile and run problem 2 tests
ocamlc -g -annot -c mltest.ml        # compile various codes
ocamlc -g -annot -c abovefuncs.ml
./process-mltest.awk test_abovefuncs.ml > test_abovefuncs.mlt
ocamlc -g -annot -c test_abovefuncs.mlt
ocamlc -g -annot -o test_abovefuncs mltest.cmo abovefuncs.cmo test_abovefuncs.cmo
===TESTS for P2 abovefuncs.cmo===
./test_abovefuncs                    # run the testing program
Found 10 tests
RUNNING 10 tests
==================================================
Test  1: ok
Test  2: FAIL                        # failure
--------------------------------------------------
test_abovefuncs.ml:31                # test file line 31; code leading to it shown
24:  (* array_above on short int array *)
25:  let thresh = 0 in
26:  let input = [|4; -2; -1; 7; 0; 3|] in
27:  let original = Array.copy input in
28:  let actual = array_above thresh input in
29:  let expect = [|4; 7; 3|] in
30:  let msg = sprintf "Expect: %s\nActual: %s" (intarr2str expect) (intarr2str actual) in
31:  __check__ (actual = expect);    # this check failed

Expect: [|4; 7; 3|]                  # expected results
Actual: [|4; 7|]                     # result actually produced by the program
--------------------------------------------------
Test  3: FAIL
--------------------------------------------------
test_abovefuncs.ml:57                # another failed test
50:  (* array_above on short float array *)
51:  let thresh = 1.5 in
52:  let input = [|4.2; 0.5; 1.2; 7.6; 8.9; 0.8; 8.5|] in
53:  let original = Array.copy input in
54:  let actual = array_above thresh input in
55:  let expect = [|4.2; 7.6; 8.9; 8.5|] in
56:  let msg = sprintf "Expect: %s\nActual: %s" (floatarr2str expect) (floatarr2str actual) in
57:  __check__ (actual = expect);

Expect: [|4.2; 7.6; 8.9; 8.5|]
Actual: [|4.2; 7.6; 8.9|]
--------------------------------------------------
Test  4: FAIL
--------------------------------------------------
test_abovefuncs.ml:95                # another failed test
88:  (* array_above on short bool array *)
89:  let thresh = false in
90:  let input = [|false; true; false; true; true;|] in
91:  let original = Array.copy input in
92:  let actual = array_above thresh input in
93:  let expect = [|true; true; true|] in
94:  let msg = sprintf "Expect: %s\nActual: %s" (boolarr2str expect) (boolarr2str actual) in
95:  __check__ (actual = expect);

Expect: [|true; true; true|]
Actual: [|true; true|]
--------------------------------------------------
Test  5: ok
Test  6: ok
Test  7: ok
Test  8: ok
Test  9: ok
Test 10: ok
==================================================
 7 / 10 tests passed

> ./test_abovefuncs 3                # re-run only problem 2's test #3
Found 10 tests
RUNNING 1 tests
==================================================
Test  3: FAIL
--------------------------------------------------
test_abovefuncs.ml:57
50:  (* array_above on short float array *)
51:  let thresh = 1.5 in
52:  let input = [|4.2; 0.5; 1.2; 7.6; 8.9; 0.8; 8.5|] in
53:  let original = Array.copy input in
54:  let actual = array_above thresh input in
55:  let expect = [|4.2; 7.6; 8.9; 8.5|] in
56:  let msg = sprintf "Expect: %s\nActual: %s" (floatarr2str expect) (floatarr2str actual) in
57:  __check__ (actual = expect);

Expect: [|4.2; 7.6; 8.9; 8.5|]
Actual: [|4.2; 7.6; 8.9|]
--------------------------------------------------
==================================================
 0 /  1 tests passed

> make clean                         # remove all compiled/auxiliary files
rm -f *.cmo *.cmi *.mlt *.annot  test_sumfuncs test_abovefuncs test_revfuncs 

> make                               # make all of the required files
ocamlc -g -annot -c sumfuncs.ml
ocamlc -g -annot -c revfuncs.ml
ocamlc -g -annot -c abovefuncs.ml

> make test                          # test all of the required files
...
===TESTS for P1 sumfuncs.cmo===
./test_sumfuncs
Found 10 tests
RUNNING 10 tests
==================================================
Test  1: ok
...

===TESTS for P2 abovefuncs.cmo===
./test_abovefuncs
Found 10 tests
RUNNING 10 tests
==================================================
Test  1: ok
...

===TESTS for P3 revfuncs.cmo===
./test_revfuncs
Found 10 tests
RUNNING 10 tests
==================================================
Test  1: ok
...

3 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.

Assignment Files

File State Notes
Makefile Provided Build file to compile all programs
mltest.ml Provided Testing utilities/main routine
process-mltest.awk Provided Awk script to add debug info to test files
sumfuncs.ml Create Problem 1 source file; create and add functions
test_sumfuncs.ml Provided Problem 1 testing file
abovefuncs.ml Create Problem 2 source file; create and add functions
test_abovefuncs.ml Provided Problem 2 testing file
revfuncs.ml Create Problem 3 source file; create and add functions
test_revfuncs.ml Provided Problem 3 testing file

General Grading Criteria   grading

Weight Criteria
5 CORRECT SUBMISSION
  All files submitted and properly zipped
  make test compiles all code and runs tests
   
5 STYLE
  Code is indented reasonably well to indicate scopes
  Comments provide insight for intricate blocks

4 Problem 1: Summing int Sequences

This classic problem requires the definition of two summing functions: one on arrays and one on lists. It will contrast the imperative/iterative style on arrays against the pure/recursive style associated with lists.

4.1 sumfuncs.ml Top-Level Definitions

Create a source file sumfuncs.ml which will have the following bindings in it.

let array_sum arr = ... ;;
(* val array_sum : int array -> int
   Return the sum of int array arr. Uses Array.length to calculate its
   length. Uses a ref to a summing int and a loop over the array
   elements. 

   REPL EXAMPLES:
   # array_sum [|1; 3; 5|];;
   - : int = 9
   # array_sum [|4; -3; 12; 2|];;
   - : int = 15
   # array_sum [||];;
   - : int = 0   
*)

let rec list_sum lst = ... ;;
(* val list_sum : int list -> int
   Return the sum of int list lst. Uses recursion and NO mutation.
   Uses List.hd and List.tl to get the head and tail of a list. 

   REPL EXAMPLES:
   # list_sum [1; 3; 5];;
   - : int = 9
   # list_sum [4; -3; 12; 2];;
   - : int = 15
   # list_sum [];;
   - : int = 0   
*)

Quick Start Tip:

  • Copy the source code above which is can almost compile.
  • Replace any ... with a valid return values for the type of the function.
  • In this case, an int is expected so something like
    let array_sum arr =
      0                 (* returns 0 *)
    ;;
    

    and a similar change for list_sum will result in code that can be run against the tests.

  • Try compiling on the command line with
    make sumfuncs.cmo
    

    and ensure that no errors are reported.

  • Try running the automated tests against the code via
    make test-p1
    

    You will likely see may errors, but compatibility with the tests allows you to see progress as your code improves.

4.2 Implementing array_sum via Iteration and Refs

Aside from some cosmetic changes, this function should end up feeling similar to its implementation in other programming languages.

  • Iteration is the easiest and mutation are the easiest means to solve implement the function.
  • Make use of a ref to an int; it can be accessed using the ! operator and assigned using the := operator
  • Note that array lengths can be determined using the module function Array.length arr
  • Acquaint yourself with OCaml's syntax for array access: arr.(i). Keep in mind that they are 0-indexed
  • Make use of a loop using the for/do/end syntax
  • DO NOT make use of any higher-order functions such as folding at this time. Show you can write imperative code by hand.

4.3 Implementing list_sum via Recursion

This function will require some more thought as list elements cannot be directly indexed via an standard syntax. This rules out a loop and should instead cause lead one towards recursion.

  • Note that the binding should start let rec list_sum .... The keyword rec allows the name being defined to be used in its own definition, a quintessential element of recursion.
  • Use an if/else or other conditional structure to analyze cases
    • An empty list has sum 0
    • A list with at least one element (a head) should add the head value on to the result of summing the remainder of the list
  • Make use if list functions to deal with the recursive case. Careful as calling these on an empty list will result in an exception.
    • List.hd will retrieve the first element of a list
    • List.tl will return the remainder of list after the first element
  • DO NOT make use of any higher-order functions such as folding at this time. Show you can write recursive code by hand.
  • DO NOT use any refs, mutation, or imperative code; no for or while looping

4.4 Grading Criteria for sumfuncs.ml   grading

The following criteria will be checked in this problem.

Weight Criteria
  MANUAL INSPECTION for sumfuncs.ml
10 array_sum
  Proper use of imperative features including refs, loops
  Use of array features to access length and individual elements
  Adherence to constraints: no higher order functions employed
   
10 list_sum
  Proper definition of a recursive function
  Case analysis to deal with base and recursive cases
  Use of List.hd and List.tl functions to dissect a list
  Adherence to constraints: no imperative code, looping, or mutation
  AUTOMATED TESTS for sumfuncs.ml
10 make test-p1 correctly compiles and passes tests

5 Problem 2: "High-Pass" Filtering Sequences

This is another classic problem which contrasts arrays and lists. The type signatures of the two required functions are polymorphic:

val array_above : 'a -> 'a array -> 'a array  (* Works on any kind of array *)
val list_above  : 'a -> 'a list -> 'a list    (* Works on any kind of list  *)

Polymorphic means here that any element type such as int, bool, float etc. will work for the functions. This often happens with structural algorithms with re-arrange a data structure. ML-family languages like OCaml handle polymorphism of this sort in a particularly satisfactory way compared to other typed languages such as C/C+/Java which either require use of awkward template syntax or worse yet specification of separate functions for every type possible. ML also preserves strong type safety over untyped languages like Python/Lisp/Lua.

5.1 abovefuncs.ml Top-Level Definitions

Create a source file abovefuncs.ml which will have the following bindings in it.

let array_above thresh arr = ... ;;
(* val array_above : 'a -> 'a array -> 'a array

   Creates a new array which has only elements which are greater than
   parameter thresh in it. Elements from arr that are larger than
   thresh appear in the same order the return array as they do in arr.
   Uses two passes to count the elements above in arr, allocates
   another array of appropriate size, and then copies in elements from
   arr.  Does not modify the original array arr.

   REPL EXAMPLES:
   # array_above 0 [|0; 1; 2; 0|];;
   - : int array = [|1; 2|]
   # array_above 0 [|4; -2; -1; 7; 0; 3|];;
   - : int array = [|4; 7; 3|]
   # array_above 3 [|4; -2; -1; 7; 0; 3|];;
   - : int array = [|4; 7|]
   # array_above 1.5 [|4.2; 0.5; 1.2; 7.6; 8.9; 0.8; 8.5|];;
   - : float array = [|4.2; 7.6; 8.9; 8.5|]
   # array_above 0.0 [|4.2; 0.5; 1.2; 7.6; 8.9; 0.8; 8.5|];;
   - : float array = [|4.2; 0.5; 1.2; 7.6; 8.9; 0.8; 8.5|]
   # array_above 9.0 [|4.2; 0.5; 1.2; 7.6; 8.9; 0.8; 8.5|];;
   - : float array = [||]
   # array_above false [|false; true; false; true; true;|];;
   - : bool array = [|true; true; true|]
*)

let rec list_above thresh lst = ... ;;
(* val list_above : 'a -> 'a list -> 'a list

   Create a list which has only elements from lst that are larger than
   thresh.  Uses recursion to accomplish this in a single pass over
   the original list. Does not modify the original list lst.

   REPL EXAMPLES:
   # list_above 0 [0; 1; 2];;
   - : int list = [1; 2]
   # list_above 0 [0; 1; 2; 0];;
   - : int list = [1; 2]
   # list_above 0 [4; -2; -1; 7; 0; 3];;
   - : int list = [4; 7; 3]
   # list_above 3 [4; -2; -1; 7; 0; 3];;
   - : int list = [4; 7]
   # list_above 1.5 [4.2; 0.5; 1.2; 7.6; 8.9; 0.8; 8.5];;
   - : float list = [4.2; 7.6; 8.9; 8.5]
   # list_above 0.0 [4.2; 0.5; 1.2; 7.6; 8.9; 0.8; 8.5];;
   - : float list = [4.2; 0.5; 1.2; 7.6; 8.9; 0.8; 8.5]
   # list_above 9.0 [4.2; 0.5; 1.2; 7.6; 8.9; 0.8; 8.5];;
   - : float list = []
   # list_above false [false; true; false; true; true;];;
   - : bool list = [true; true; true]
*)

5.2 array_above via Iteration

  • Note that the function must be polymorphic: it works with any type of array. The greater than operator > is polymorphic to enable this.
  • Make use of two "passes" through the data; i.e. loop once through the array then again.
  • In the first pass, set up a ref to an integer and simply count how many elements are above the threshold given. Use an if condition for this.
  • After determining the number of elements, allocate an array of appropriate size using the syntax Array.make length x. Here x is an item to fill the array with so; thresh is a good choice.
  • In the second pass, insert every element above the threshold into the created array. This will require tracking the index for insertion likely using another ref.
  • OCaml is a bit persnickety about multiple expressions in if/else statements, particularly multiple side-effects only statements. The begin/end syntax is useful for this as shown below.
    if condition then
      begin
        sideeffect stuff;
        sideeffect stuff;
       end
    
  • DO NOT make use of any higher-order functions such as folding at this time. Show you can write imperative code by hand.

5.3 list_above via Recursion

  • Similar to the array version, this function is polymorphic so should work with any type of list
  • let rec above suggests that a recursive definition should be used.
  • You will need to use the double colon :: "cons" operator to construct a list. This operator attaches an element to the front of an existing list to produce a new list as shown in the below REPL session.
    # let l1 = [8; 6; 7];;
    val l1 : int list = [8; 6; 7]
    # let e1 = 3;;
    val e1 : int = 3
    # let l2 = e1 :: l1;;           (* tack 3 onto the front *)
    val l2 : int list = [3; 8; 6; 7]
    # let l3 = 19 :: l2;;           (* tack 19 onto the front *)
    val l3 : int list = [19; 3; 8; 6; 7]
    # l2;;                          (* l2 has not changed *)
    - : int list = [3; 8; 6; 7]
    
  • DO NOT make use of any higher-order functions such as folding at this time. Show you can write recursive code by hand.
  • DO NOT use any refs, mutation, or imperative code; no for or while looping

5.4 Grading Criteria for abovefuncs.ml   grading

The following criteria will be checked in this problem.

Weight Criteria
  MANUAL INSPECTION for abovefuncs.ml
10 array_above
  Proper use of imperative features including refs, loops
  Clear use of first pass to count elements
  Allocation of array for return
  Clear second pass adding in elements to return array
  Adherence to constraints
   
10 list_above
  Proper definition of a recursive function
  Case analysis to deal with base and recursive cases
  Use of List.hd and List.tl functions to dissect a list
  Use of cons operator :: to construct new list
  Adherence to constraints: no imperative code, looping, or mutation
  AUTOMATED TESTS for abovefuncs.ml
10 make test-p2 correctly compiles and passes tests

6 Problem 3: Reversing Sequences

6.1 revfuncs.ml Top-Level Definitions

Create a source file revfuncs.ml which will have the following bindings in it.

let array_rev arr = ... ;;
(* val array_rev : 'a array -> unit

   Reverses the given array in place. Uses iteration and mutation to
   do so efficiently. DOES NOT generate any internal copies of the
   parameter array.

   REPL EXAMPLES:
   # let a1 = [|1; 2; 3;|];;
   val a1 : int array = [|1; 2; 3|]
   # array_rev a1;;
   - : unit = ()
   # a1;;
   - : int array = [|3; 2; 1|]
   # let a2 = [|"a"; "b"; "c"; "d"; "e"; "f"|];;
   val a2 : string array = [|"a"; "b"; "c"; "d"; "e"; "f"|]
   # array_rev a2;;
   - : unit = ()
   # a2;;
   - : string array = [|"f"; "e"; "d"; "c"; "b"; "a"|]
   # let a3 = [|true; true; false; false; true;|];;
   val a3 : bool array = [|true; true; false; false; true|]
   # array_rev a3;;
   - : unit = ()
   # a3;;
   - : bool array = [|true; false; false; true; true|]
*)

let list_rev lst = ... ;;
(* val list_rev : 'a list -> 'a list

   Return a reversed copy of the given list. Does not (and cannot)
   modify the original list. Uses an internal recursive function to
   build the reversed list. The internal function is tail-recursive.

   REPL EXAMPLES:
   # list_rev lst1;;
   - : int list = [3; 2; 1]
   # lst1;;
   - : int list = [1; 2; 3]
   # let lst2 = ["a"; "b"; "c"; "d"; "e"; "f"];;
   val lst2 : string list = ["a"; "b"; "c"; "d"; "e"; "f"]
   # list_rev lst2;;
   - : string list = ["f"; "e"; "d"; "c"; "b"; "a"]
   # lst2;;
   - : string list = ["a"; "b"; "c"; "d"; "e"; "f"]
   # let lst3 = [true; true; false; false; true];;
   val lst3 : bool list = [true; true; false; false; true]
   # list_rev lst3;;
   - : bool list = [true; false; false; true; true]
   # lst3;;
   - : bool list = [true; true; false; false; true]
*)

6.2 Implementing array_rev via Iteration / Assignment

  • Note that the type of the function returns unit; this is an indication that side-effects will be used, in this case modification of the parameter array.
  • Make use of the array assignment syntax
    arr.(i) <- elem;
    

    This mutates an array in place

  • A loop will be required to traverse elements of the array.
  • DO NOT make a copy of the array to achieve the reversal. Instead, repeatedly swap elements in the array. In an array of 10 elements indexed 0 to 9, this would mean swapping as follows:
    • 0th and 9th
    • 1th and 8th
    • 2nd and 7th
    • etc.
  • DO NOT make a copy of the array: operate on it in place
  • DO NOT make use of any library functions for reversal

6.3 Implementing list_rev via Recursion

  • Note that the type of this function returns a list of the same kind as the original. It does not mutate the original
  • Make use of a locally defined recursive helper function to traverse the list
  • The helper should take two parameters, the remainder of the original list and a reversed list that is being constructed
  • At each step in the recursive helper
    • Check if the end of the original list has been reached and return the reversed list if so
    • Otherwise, Peel off the head and tail of the original list
    • Attach the head to the reversed list being built
    • Recurse on the remainder (tail) of the original list with the slightly larger reversed list as the other argument
  • After defining it, call the recursive helper with the entire original list as the first argument and an empty reversed list as the second argument
  • DO NOT employ any mutation, global variables
  • DO NOT make use of any library functions for reversal

6.4 Grading Criteria for abovefuncs.ml   grading

The following criteria will be checked in this problem.

Weight Criteria
  MANUAL INSPECTION for revfuncs.ml
10 array_rev
  Proper use of imperative features including refs, loops
  Clear use of repeated swapping to accomplish reversal
  No internal array allocated for use: operating on parameter in place
  Correct unit type returned
  Adherence to constraints
   
10 list_rev
  Proper definition of a recursive helper function
  Case analysis to deal with base and recursive cases
  Use of List.hd and List.tl functions to dissect a list
  Use of cons operator :: to construct new list
  Adherence to constraints: no imperative code, looping, or mutation
  Helper function is properly called and results returned
  Recursive helper is tail recursive
  AUTOMATED TESTS for sumfuncs.ml
10 make test-p3 correctly compiles and passes tests

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


Author: Chris Kauffman (kauffman@umn.edu)
Date: 2018-09-14 Fri 16:51