Homework 7
Last updated: Thu, 26 Mar 2026 17:42:54 -0400
Out: Tue Mar 24 2026, 11am EST
Due: Tue Mar 31 2026, 11am EST
Overview
This assignment begins to look at more complicated recursive data definitons, e.g., intertwined data, which are common in real world applications.
This hw will be graded accordingly:
correctness (Autograded) (10 pts)
design recipe (16 pts)
testing (16 pts)
style (16 pts)
README (2 pt)
Setup
Create a new repository for this assignment by going to the CS450 Spring 2026 GitHub Organization and clicking "New".
Note: The CS450 Spring 2026 GitHub Organization must be the owner of the repository. Please do not create the repository in your own account.
On the "Create a new repository" screen:
Name the repository hw<X>-<LASTNAME>-<FIRSTNAME> where <X> is the current homework number.
For example, I would name my hw7 repository hw7-Chang-Stephen.
Mark the repository as Private.
Check "Add a README file".
Select the Racket template for the .gitignore.
Choose whatever you wish for the license.
When done click "Create repository".
Updating Racket450
Make sure you have the latest version of racket450.
To do this from DrRacket, go to File -> Package Manager -> Currently Installed, search for "racket450", and then click "Update".
Alternatively, if you prefer the command line, run:
raco pkg update racket450
Reading
Read Chapters 19-20 of the Textbook (where reading means trying to work through the examples and exercises interactively).
NOTE: The textbook will refer to "Student Languages" which we do not use in this course (and a "Stepper" that only works with the Student Languages). Instead, we use a version of Racket tailored for this course, which is invoked by putting #lang racket450 at the top of a file (see also Before Submitting).
Also, read any relevant sections of the The Design Recipe section of the course website (topics that will be covered in future lectures are marked as such).
Tasks
The main code should go in a file named hw7.rkt that uses #lang racket450, as described previously.
NOTE, on using previous code: All assignments are designed so that it is quicker to complete them if you start from scratch and follow the Design recipe. No previous code or "solutions" to previous assignments are needed to complete any assignment. Do not attempt to complete this assignment by starting with some pile of code (from a previous assignment or anywhere else) and trying to "make it work". Doing this is almost always slower and you might not be able to finish the assignment on time if you do it this way (it also leads to tedious bugs that are hard to find and impossible to give help to). Finally, not following these instructions demonstrates a lack of understanding of course concepts—
which focuses on the high-level programming process and not the final code— and thus will receive a low grade.
NOTE, on not automatically running code: The submitted program must be only a series of defines (both constants and function definitions are allowed). It should not run any code other than check-equal? Examples. Not following this will result in GradeScope errors and/or timeouts.
As usual, all submitted code must follow the The Design Recipe. This means that language features may only be used in the correct scenarios, as called for by The Design Recipe.
For example, set! and other "imperative" features are not allowed ever.
Conditionals such as if and cond are only to be used with the appropriate Data Definitions or in other appropriate scenarios described in class.
Signatures should use define/contract and the predicates defined in the Data Design Recipe step. In this assignment, you may use the listof contract constructor where appropriate.
For Examples and Tests, do not use check-expect from the Beginning Student Language (even though the textbook says to). Instead, use check-equal? or other testing forms from rackunit (which are built into racket450, so do not explicitly require rackunit).
Examples for a function definition should be put before the define in hw7.rkt.
Tests should be put a into hw7-tests.rkt file that uses #lang racket450/testing. Try to think about corner cases and code coverage.
NOTE, on one-line helper functions: If the name and description of a "helper" function clearly describe what it does, and it clearly follows some Data Definition and all other Design Recipe steps (the course staff is the final arbiter of this), it does not need to be submitted with Examples and Tests if they are covered by other tests. ("Helper" functions are defined as functions not described in the homework assignment description.) NOTE: This does not change the Design Recipe. It is only changing submission requirements. As usual, however, we will not be able to help debug code that does not follow the Design Recipe, so omit these steps at your own risk.
All other functions should have at minimum one Example and "sufficient" Tests.
Data Definitions
Arguably the most common data type today is the multidimensional "array", as found in popular libraries like Python’s NumPy, which is used in a multitude of data science and machine learning applications. This assignment will work with (a simplified version of) this type of data. Specifically, define the following Data Definitions for this assignment:
- An Array is one of:
Number
ArrayList
An ArrayList is one of:UPDATE: In addition, an Array must be "rectangular", meaning that along any one dimension, each element must have the same length. For example, in a 2d Array, all the rows must have the same length.
Together, these intertwined data definitions, plus the invariants, represent all possible rectangular multi-dimensional array values.
Though we have defined Array as a (potentially nested) list (of lists), to show the connection to existing libraries (and introduce some new Racket features), we will use an alternate constructor that resembles the array constructor in NumPy (Note: familiarity with this library it not needed to do this assignment).
Specifically, in python, one might write:
arr = numpy.array([1, 2, 3, 4, 5])
In this assignment, define an alternate mk-array constructor that returns its argument unchanged (for now, though we can later add other parameters like the python constructor has). When used, the given argument should use the racket quote constructor (with square brackets, which are the same as round ones in Racket). So to define the same array as above in racket450, we write:
(define arr (mk-array '[1 2 3 4 5]))
Multi-dimensional arrays are just as straightforward.
In python:
arr = numpy.array([[1, 2, 3], [4, 5, 6]])
arr = numpy.array([[[1, 2, 3], [4, 5, 6]], [[1, 2, 3], [4, 5, 6]]])
In this assignment;
(define arr (mk-array '[[1 2 3] [4 5 6]]))
(define arr (mk-array '[[[1 2 3] [4 5 6]] [[1 2 3] [4 5 6]]]))
- An Index is one of :
a nonnegative Int
a negative Int
Represents the (0-based) n-th element in an Array. For negative indices, -1 is the last element, -2 second to last, etc. - A Slice is one of:
Index
- a (itemization of?) compound data containing
a start Index (default 0)
an stop Index (default last element)
a "step" (default 1) which allows over skipping over elements of an Array, e.g., step=2 returns every other element, etc
You will have to decide on the details of this data definition. Note that for the compound data case, the exact value might not be known until the Slice is used. Thus you will need to decide on a representation for these scenarios You are allowed to define any other Data Defintions as you see fit
Array Functions
You will need to write the following functions that compute on Array. Since the underlying representation is a (list of) lists, you may use list functions from racket/list such as map, filter, foldr/foldl, etc whenever possible (take and drop are other functions that may be useful in this assignment). But you will also need to write some function that follow the proper template, e.g. Array:
To make things easier, we will use NumPy as a reference for expected behavior. Thus, you can refer to any implementation, e.g. this online implementation, to help you understand the required functions and create Examples.
a function ndim with Signature represented by contract (-> Array? exact-nonnegative-integer?) that computes the number of dimensions of the Array. The number of dimensions is equal to the nestedness of the lists.
a function shape with Signature represented by contract (-> Array? (listof exact-nonnegative-integer?)) that computes the length of the given Array along each of its dimensions. In other words, the result must satisfy the invariant that (= (ndim arr) (length res)) where arr is input Array and res is the output.
a function ref with Signature represented by contract (-> Array? (listof Index?) number?) (-> ArrayList? (listof Index?) Array?) that returns the elements in the (non-scalar) Array referenced by the given Indexes. You may assume the additional invariant that (= (ndim arr) (length indices)) (<= (length indices) (ndim arr)) where arr is the first argument and indices is the second argument. In other words, there are less indices (or the same) than dimensions.
This function should be straightforward if you follow the Design Recipe and use the appropriate template. But it might be difficult if you do not. You may also find it useful to define an "index fn" that converts negative indices to positive ones. You should not attempt to do everything with one giant function.
a function slice1 with Signature represented by contract (-> ArrayList? Slice? Array?). Outputs the elements of the given (non-scalar) Array specified by the given Slice, but only for the first (outermost) dimension. In other words, the function can mostly treat the input Array as a flat list.
a function filter/step with Signature represented by contract (-> list? exact-nonnegative-integer? list?) that returns the elements of the input list according to "step" represented by the second argument. If the input "step" is 1, then the input list should be unchanged. If "step" is 2, then the output should contain every other element (starting with the first one). The easiest way to implement this function is with an accumulator that keeps track of something like "number of elements to skip" or something similar.
API for Grading
Finally, define the following additional functions so your submission may be properly tested and graded for correctness. These functions do not need to follow the Design Recipe, e.g., you may combine several templates and you don’t need to submit Examples or Tests (though obviously the Autograder tests won’t pass if the functions don’t work).
mk-Slice/testing: this function should take three optional keyword arguments, #:start, #:stop, and #:step. If any of the arguments are omitted, then a suitable default should be used, according to your Slice data definition.
Before Submitting
Testing (and Autograders)
Before submitting, note:
Each programmer is solely responsible for testing their program to make sure it’s correct. Do not submit until all code has been has a "sufficient" number of Test cases that verify its correctness.
Note that there is no GradeScope "Autograder" available for students to use (an Autograder is not a software development/testing tool anyways, so it should not be used as one).
Thus, no questions mentioning an Autograder will be answered, e.g., posts asking "why is the Autograder giving an error?" are not allowed.
If you happen to find an Autograder and decide to look at its output despite this warning, please understand that it may be incorrect or incomplete, change at any time, or have random behavior, and that it in no way indicates the grade of the submitted hw.
Anyone that does get useful information from an Autograder, e.g., a failing test case or crashing code report, should treat it as bonus information (that you otherwise would not have had) that you and you alone must determine what to do with.
Regardless of what any Autograder might say, all code must still be independently tested to be correct before it is submitted.
The proper way to ask questions is with small code examples. This means that each question must include a small example code snippet along with what the "expected" result should be!
Further, any posted examples should contain the minimal amount of code needed to explain the question. Full file dumps or anything more than a few lines will not be accepted. More is not better. In fact it’s worse because it takes longer to read and is less likely to get a good answer.
Style
All code should follow proper Racket Style.
Also, the repository itself must follow proper style. Specifically, it must have appropriate commit messages. See How to Write a Git Commit Message if you are unsure how to write a commit message.
Note: Do not use the "file upload" feature on Github. The course staff may not accept hw uploaded in this way.
Files
A submission must have the following files in the repository root:
hw7.rkt: Contains the hw solution code.
The first line should be #lang racket450.
All defines should use the name specified in the exercise (ask if you are unsure).
hw7-tests.rkt: This file should use the #lang racket450/testing language.
It should also require hw7.rkt and define tests for it.
Specifically, it should contain "sufficient" Test cases (e.g., check-equal?, etc.) for each defined function.
README.md: Contains the required README information, including the GitHub repo url.
Submitting
When you are done, submit your work to Gradescope hw7. You must use the "GitHub" Submission Method and select your hw<X>-<LASTNAME>-<FIRSTNAME> repository.
Note that this is the only acceptable way to submit homework in this course. (Do not manually upload files and do not email files to the course staff. Homework submitted via any unapproved methods will not be graded.)

