Submission Details

DrRacket Cheat Sheet
Checking for Errors
Submission FAQ
Autograder FAQ
Late Penalty Waiver

For this assignment, you’ll make a simple collection of information about music tracks (we’re calling this a library). This library is just a list of track objects - (listof track) - so anytime you see library think (listof track). Feel free to use your own tracks, other people’s tracks, or just make them up (Note that this is different than the pre-recorded lecture examples which deal with albums). Finally, you’ll write expressions and functions that query some particular library for different kinds of information.


Important

Throughout this assignment, as with any assignment, you are encouraged when possible to reuse functions from one problem to solve subsequent problems. However, don’t just COPY AND PASTE CODE. Instead…call the function by name! That simultaneously saves you work and makes the code easier to read. Plus, if you find a bug later, you only have to fix it in one place: the original definition.

In general, these problems only require simple answers. Most have two or three line answers, although the exact number of lines depends on how you put line breaks into your code. That’s not to say that if you have an answer that’s longer that it’s wrong, but it may mean that you’re making life unnecessarily difficult for yourself. One good way to make life easier on yourself is to reuse code you’ve already written.

Write your functions to work with any input. Don’t assume our test data will look like your test data. Follow these instructions exactly. If we say to remove duplicates, make sure you remove duplicates. Make sure your type signatures exactly match the ones in the assignment as you go.

Exercise 3 Starter Files


Part 0. Making Your Library

You’ll be adding your code to the exercise_3.rkt we’ve provided for you. Each track object has three fields: the title, the artist name, and the genre (e.g. “Pop”, “Rock”, “Acid-house”, “Country”, whatever). We’ve provided the track data definition for you at the top of the file:

(define-struct track (title artist genre))
How to use the track type? The define-struct form automatically creates the following functions for the track type:
  • make-track is a function for creating instances of track. For example, to create n track object with title "Orinoco Flow", artist "Enya", and genre "New-age", we would do the following:
    (make-track "Orinoco Flow" "Enya" "New-age")
    
  • track-<field-name> (for each field) functions for accessing values of each field in a track object. This would mean we'd have the functions track-title, track-artist, and track-genre. The following example creates a track and then accesses its fields:
    (define orinoco-flow (make-track "Orinoco Flow" "Enya" "New-age")
    (track-title orinoco-flow) ; returns "Orinoco Flow"
    (track-artist orinoco-flow) ; returns "Enya"
    (track-genre orinoco-flow) ; returns "New-age"
    
  • track? is a predicate (function which returns a boolean) that identifies whether a value is a track object. It returns true when given a track object and false otherwise. Using the orinoco-flow track defined in the previous example:
    (track? orinoco-flow) ; returns true
    (track? 111) ; returns false
    (track? "hi") ; returns false
    

Fill in the definitions for hip-hop-lib and pop-lib in your definitions file:

  1. Define the library hip-hop-lib that has exactly one track object. The genre of this object should be "Hip-hop".
  2. Define the library pop-lib that has exactly three track objects. The genre of these three objects should be "Pop". Moreover, there should be exactly two distinct artists. Put differently, two of the three track objects should be composed/sung by the same person.
Hint Type something like this (but with the correct genres):
; a library is a (listof track)
; a track is a (make-track string string string)

(define hip-hop-lib
  (list (make-track "N.Y. State of Mind" "Nas" "Hip-hop")))

(define pop-lib
  (list (make-track "Pyramids" "Frank Ocean" "R&B")
        (make-track "Call Me Maybe" "Carly Rae Jepsen" "Pop")
        ...etc...))
Why these libraries? One of the questions involves writing a function that finds all the tracks of a given genre. If all the tracks in the library are in the rock genre, then there's only one genre and when you ask for all the pop tracks and it gives back all the tracks, you don't know whether that's because the code really works, or because it's not even paying attention to the genre.

Therefore, you want to make sure there are multiple artists and genres, some artists with only one track or genre, others with multiple artists or genres, etc. As an example, you can use (append hip-hop-lib pop-lib) to create a library that has exactly two genres.

Again, they may not all be your tracks or even actual tracks you own or exist! But you need to make sure there are enough tracks and that they are varied in the right ways to test your code. For example, one of the functions you will write is intended to find artists who work in multiple genres which means you need to make sure there’s at least one artist in the library who does work in multiple genres and at least one who does not. That way you can check that the one who does appears in the output but the one who only works in one genre doesn’t appear in the output. Feel free to create more libraries other than hip-hop-lib and pop-lib as you go through the assignment if you realize you need more data to properly test your code.

Note that we will be testing your code against our own library, not yours. So in your tests you should test against a couple of different libraries. We’ve also included several exemplar libraries in the file for your reference.


Part 1. all-titles

Write a function to find all the titles in a library. Call this function all-titles. It should take a (listof track) (a library) as input, so your answer will look something like (you fill in the expression):

; all-titles : (listof track) -> (listof string)
; Gets all titles of all tracks in the library
(define all-titles
   (lambda (lib) …fill this in…))

(check-expect (all-titles (list (make-track "a" "somebody" "rock")
                                (make-track "b" "somebody 2" "country")))
              (list "a" "b"))

Hint: Here you are transforming a list of tracks into a list of titles.


Part 2. all-artists

Now write one to find all the artists in the library. Call this one all-artists. Again, this function will take a library as input. Its signature is:

; all-artists: (listof track) -> (listof string)

It’s important that each artist should appear only once in the output. Be sure to write a test case (a check-expect) for all-artists that verifies this (give it a library where there are two tracks by the same artist and verify the artist name only appears once in the output).

You can use the remove-duplicates function (which we’ve provided for you) to take a list that has the same items repeated many times and gives you back a new list that only has the one copy of each item. So if we run:

> (remove-duplicates (list "a" "b" "c" "a"))

We get the result:

(list "a" "b" "c")

Part 3. all-genres

Now write a function, all-genres, to return all the genres, again with each genre mentioned only once. Again, write at least one test case for it so that you know it works. Its signature is:

; all-genres: (listof track) -> (listof string)

Part 4. artist-tracks

Write a function, artist-tracks, that takes an artist name and library as inputs and outputs all the tracks by that artist. That is,

(artist-tracks "K.Flay" some-library)

should return a list with all the tracks by K.Flay. Again, write test cases to verify the operation of this function. This should output the track objects, not just the titles of the tracks!

Hints! You will need to use filter, and you will need put a lambda expression inside of the call to filter, so your code will look roughly like:
;; artist-tracks : string (listof track) -> (listof track)
;; Get all tracks in the library by the given artist
(define artist-tracks
   (λ (desired-artist lib)
     (filter (λ (track) ...fill this in...)
             lib)))
; your tests here
Note, you can’t do this by calling filter with the name of a function you’ve defined separately, as in:
(define is-the-right-artist?
  (λ (track) ...some magic code...))
(define artist-tracks
  (λ (desired-artist lib)
    (filter is-this-the-right-artist? lib))
because the part that says "...some magic code..." would need to use the variable desired-artist. But only code inside of artist-tracks can access the desired-artist variable. So use a λ expression inside the call to filter as we did above.

Part 5. artist-genres

Now write a function, artist-genres, to return all the genres of a given artist (without duplicates).

Its signature is:

; artist-genres: string (listof track) -> (listof string)

Again, you should write at least one test case (i.e. a check-expect), for this and all other problems.

Hint: Just call artist-tracks to find the tracks by the artist. Don’t rewrite code to do something you’ve already written code for!


Part 6. artist-is-multi-genre?

Now write a function, artist-is-multi-genre?, that takes the name of an artist and the library as inputs and returns true if an artist has tracks in more than one genre. Its signature is:

; artist-is-multi-genre?: string (listof track) -> boolean

Hint: Your solution should call artist-genres.


Part 7. multi-genre-artists

Now write a function, multi-genre-artists, that returns a list of the names of all artists who work in more than one genre. Its signature is:

; multi-genre-artists: (listof track) -> (listof string)

Your solution should call artist-is-multi-genre?.


Part 8. artist-track-counts

Write a function, artist-track-counts, to count the number of tracks by each artist. It should return a list of lists, where each sub-list is the name of an artist followed by the number of tracks they have in the library. Its signature is:

; artist-track-counts: (listof track) -> (listof (listof string number))

For example, assuming your library had two Snoop Dogg tracks, 1 Hayley Kiyoko track, and 1 Lido track (and nothing else), you’d get:

(check-expect (artist-track-counts some-library)
              (list (list "Snoop Dogg" 2)
                    (list "Hayley Kiyoko" 1)
                    (list "Lido" 1)))
Hints Start by writing a function that computes the number of tracks by a single artist.
; artist-track-count: string (listof track) -> number

Now use this function to write a function that, given the name of an artist, returns the two-element list, the artist’s name followed by their track count.
; artist-track-count-list: string (listof track) -> (listof string number)

Now use that function, along with the all-artists function, to write artist-track-counts.

Part 9. genre-track-counts

Now do the same thing, but count the number of tracks in each genre, rather than the number by each artist. Call the function genre-track-counts. Its signature is:

; genre-track-counts: (listof track) -> (listof (list string number))

Hint: start by writing a genre-tracks function, then a genre-track-count function, etc.


Common Problems

  1. Make sure that all of your functions match the provided signatures and that the function is named exactly as specified (and takes the inputs in the order specified).
  2. Make sure you follow instructions completely. For example, make sure that artist-genres calls remove-duplicates.
  3. Don’t include any references to testing-library-1 in your function definitions. This test library should only be used for your tests (i.e. check-expects)!.
  4. Remember that strings are case sensitive. So “Rock” and “rock” are different strings, and hence (string=? "Rock" "rock") is false.

Double Checking your Work

Before turning your assignment in, run the file one last time to make sure that it runs properly and doesn’t generate any exceptions, and all the tests pass. Make sure you’ve also spent some time writing your OWN check-expect calls to test your code. Remember, we will use our OWN TRACKS as test cases on your functions. That means nothing in your functions can depend on the test tracks we gave you (e.g. check to make sure you actually use your inputs; check to make sure it’s actually processing those inputs; etc.).