Playlist#
Fig. 26 A playlist of a music player.
CC BY-SA 3.0. By Thewob. Source: Wikimedia Commons#
Imagine you want to manage a playlist which supports adding and removing tracks. The playlist should be able to manage thousands of tracks.
Activity 61 (Array or not to array?)
How would you implement this with an array? In particular, how do you add, remove tracks and how do you list the tracks?
Do you see any disadvantages of arrays? If yes, do you have any improvement ideas?
Requirements#
Use the template below and only provide code in
YOUR CODE HEREblocks.Document your project, but you don’t have to provide a flowchart.
Content in
playlist.txtshould be transformed and written toplaylist-out.txt, so that you get the following output. This modification is caused by highlighted lines inmain.c.Optional: augment your code with an interactive command line interface on terminal or even a mouse-interface using raylib.
playlist.txt:
1Bad Guy – Billie Eilish 👽
2Bohemian Rhapsody - Queen 🎤
3Billie Jean – Michael Jackson 🕺
4Rolling in the Deep – Adele 🌊
5Smells Like Teen Spirit – Nirvana 🤘
playlist-out.txt:
1Bad Guy – Billie Eilish 👽
2Bohemian Rhapsody - Queen 🎤
3Billie Jean – Michael Jackson 🕺
4Tarkan – Şımarık 💋
5Rolling in the Deep – Adele 🌊
Template#
1#include "singly_linked_list.h"
2#include <stddef.h>
3#include <stdio.h>
4#include <stdlib.h>
5#include <string.h>
6#define TRACK_TITLE_SIZE 60
7
8#define PLAYLIST_IN_PATH "playlist.txt"
9#define PLAYLIST_OUT_PATH "playlist-out.txt"
10// To avoid unnecessary complexity, we fix the filenames instead of getting them
11// through runtime parameters.
12
13typedef char Data[TRACK_TITLE_SIZE];
14Node *playlist;
15
16/// Removes trailing newline from the line, if it exists.
17/// Note: Some lines may not have a newline, e.g., last line in a file,
18/// therefore we have to check for presence.
19char *remove_newline_if_exists(char *line) {
20 // YOUR CODE HERE
21 return line;
22}
23
24/// Reads lines from at `filename`, creates a node for each line and inserts
25/// nodes to `list`.
26Node **load_file(const char *filename, Node **list) {
27 // Open the file and assign to stream `f`
28 // YOUR CODE HERE
29 if (!f) {
30 perror(PLAYLIST_IN_PATH);
31 exit(EXIT_FAILURE);
32 }
33 char line[TRACK_TITLE_SIZE];
34
35 while (
36 // Read one line from the stream
37 // YOUR CODE HERE
38 ) {
39 remove_newline_if_exists(line);
40
41 auto new_node = (Node *)malloc(sizeof(Node));
42 new_node->next = nullptr;
43 auto data = (Data *)malloc(sizeof(Data));
44 new_node->data = data;
45
46 // Copy line to `new_node` and append `new_node` to `list`
47 // YOUR CODE HERE
48 }
49 fclose(f);
50 return list;
51}
52
53/// Saves `list` contents to the file at `filename`.
54void save_file(const char *filename, Node *list) {
55 // Open file
56 // YOUR CODE HERE
57
58 // Move through the list and save the tracks to the file
59 // Note: You have to cast the data to print the track to the file as follows:
60 // (char *)current->data
61 // Because current->data is a pointer to everything (void*).
62 auto current = playlist;
63 // YOUR CODE HERE
64
65 fclose(f);
66 //// END SOLUTION
67}
68
69void print_tracks(const Node *const playlist) {
70 auto current = playlist;
71 for (size_t i = 1; current; i++, current = current->next)
72 printf("%2ld: %s\n", i, (char *)current->data);
73}
74
75int main() {
76 load_file(PLAYLIST_IN_PATH, &playlist);
77 print_tracks(playlist);
78
79 // Deletion
80 size_t node_index_to_del = 4;
81 free(delete_at(&playlist, node_index_to_del));
82
83 // Insertion
84 Node node = {.data = "Tarkan – Şımarık 💋", .next = nullptr};
85 insert_at(&playlist, 3, &node);
86
87 save_file(PLAYLIST_OUT_PATH, playlist);
88}
#pragma once
#include <stddef.h>
/**
* Node of the single list
* @note Thanks to the `void` pointer the linked list can store any datatype.
*/
typedef struct Node {
void *data;
struct Node *next;
} Node;
/**
* Inserts `node` at the 0-indexed position `n` on the list with the start
* node`head`.
* @return `Node *` that has been inserted.
* @note `head` is a pointer to a pointer, because we need to be
able to modify the head of the provided linked list.
*/
Node *insert_at(Node **head, size_t n, Node *node);
/**
* @return size of the list
*/
size_t list_len(Node *head);
/**
* @return node at the index `n`
*/
Node *node_at(Node *head, size_t n);
/**
* Deletes and deallocates node at index `n`.
* @return node data
* @note Data of the node has to be deallocated manually.
*/
void *delete_at(Node **head, size_t n);
/**
* Deletes and deallocates node at index `n`.
* @return the node at the tail of the list.
* @note Can return `nullptr` if the list is empty.
*/
Node *tail(Node *head);
#include "singly_linked_list.h"
#include <stddef.h>
#include <stdlib.h>
static Node *prev_node_at(Node *head, size_t n) {
auto current = head;
Node *prev = nullptr;
if (current) {
size_t i = 0;
while (i != n && current) {
prev = current;
current = current->next;
++i;
}
if (i != n)
return nullptr;
else
return prev;
}
return prev;
}
Node *insert_at(Node **head, size_t n, Node *node) {
if (!n) {
node->next = *head;
*head = node;
return node;
}
auto prev_node = prev_node_at(*head, n);
auto next_node = prev_node->next;
prev_node->next = node;
node->next = next_node;
return node;
}
size_t list_len(Node *head) {
auto current = head;
if (current) {
size_t len = 1;
while ((current = current->next)) {
++len;
}
return len;
}
return 0;
}
Node *node_at(Node *head, size_t n) {
auto current = head;
for (size_t i = 0; i < n;) {
if (current) {
current = current->next;
++i;
} else
return nullptr;
}
return current;
}
// Node *node_idx(Node *head, size_t n);
void *delete_at(Node **head, size_t n) {
auto current = *head;
if (!n) {
if ((*head)->next)
*head = current->next;
else
*head = nullptr;
return nullptr;
} else {
auto prev = prev_node_at(*head, n);
auto node_to_be_deleted = prev->next;
prev->next = node_to_be_deleted->next;
auto data = node_to_be_deleted->data;
free(node_to_be_deleted);
return data;
}
}
Node *tail(Node *head) {
auto current = head;
if (current) {
auto next = current->next;
while (next) {
current = current->next;
next = current->next;
}
return current;
}
return current;
}