Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add simple-linked-list exercise #58

Merged
merged 6 commits into from
Jul 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions concepts/smart-pointers/.meta/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"blurb": "<todo>",
"authors": [
"misicnenad"
],
"contributors": []
}
1 change: 1 addition & 0 deletions concepts/smart-pointers/about.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Smart Pointers
1 change: 1 addition & 0 deletions concepts/smart-pointers/introduction.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Introduction
1 change: 1 addition & 0 deletions concepts/smart-pointers/links.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[]
17 changes: 16 additions & 1 deletion config.json
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,16 @@
],
"prerequisites": [],
"difficulty": 3
},
{
"slug": "simple-linked-list",
"name": "Simple Linked List",
"uuid": "cc9a6bf9-9027-42c4-a71d-082ce48f7869",
"practices": [
"smart-pointers"
],
"prerequisites": [],
"difficulty": 5
}
],
"foregone": [
Expand Down Expand Up @@ -303,7 +313,12 @@
{
"uuid": "8a4a6525-c1e4-44f5-ab6b-cf5d8ce6701e",
"slug": "mutability",
"name": "M`utability"
"name": "Mutability"
},
{
"uuid": "7e322172-04ba-43f8-aba9-8ae999372b90",
"slug": "smart-pointers",
"name": "Smart Pointers"
}
],
"key_features": [
Expand Down
4 changes: 3 additions & 1 deletion exercises/practice/beer-song/.meta/config.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
{
"authors": [],
"authors": [
"misicnenad"
],
"files": {
"solution": [
"src/lib.cairo",
Expand Down
4 changes: 3 additions & 1 deletion exercises/practice/binary-search/.meta/config.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
{
"authors": [],
"authors": [
"misicnenad"
],
"files": {
"solution": [
"src/lib.cairo",
Expand Down
4 changes: 3 additions & 1 deletion exercises/practice/rational-numbers/.meta/config.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
{
"authors": [],
"authors": [
"misicnenad"
],
"files": {
"solution": [
"src/lib.cairo",
Expand Down
19 changes: 19 additions & 0 deletions exercises/practice/simple-linked-list/.docs/instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Instructions

Write a prototype of the music player application.

For the prototype, each song will simply be represented by a number.
Given a range of numbers (the song IDs), create a singly linked list.

Given a singly linked list, you should be able to reverse the list to play the songs in the opposite order.

~~~~exercism/note
The linked list is a fundamental data structure in computer science, often used in the implementation of other data structures.

The simplest kind of linked list is a **singly** linked list.
That means that each element (or "node") contains data, along with something that points to the next node in the list.

If you want to dig deeper into linked lists, check out [this article][intro-linked-list] that explains it using nice drawings.

[intro-linked-list]: https://medium.com/basecs/whats-a-linked-list-anyway-part-1-d8b7e6508b9d
~~~~
5 changes: 5 additions & 0 deletions exercises/practice/simple-linked-list/.docs/introduction.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Introduction

You work for a music streaming company.

You've been tasked with creating a playlist feature for your music player application.
20 changes: 20 additions & 0 deletions exercises/practice/simple-linked-list/.meta/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"authors": [
"misicnenad"
],
"files": {
"solution": [
"src/lib.cairo",
"Scarb.toml"
],
"test": [
"src/tests.cairo"
],
"example": [
".meta/example.cairo"
]
},
"blurb": "Write a simple linked list implementation that uses Elements and a List.",
"source": "Inspired by 'Data Structures and Algorithms with Object-Oriented Design Patterns in Ruby', singly linked-lists.",
"source_url": "https://web.archive.org/web/20160731005714/http://brpreiss.com/books/opus8/html/page96.html"
}
97 changes: 97 additions & 0 deletions exercises/practice/simple-linked-list/.meta/example.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
#[derive(Drop, Copy)]
struct SimpleLinkedList<T> {
head: List<T>,
len: usize,
}

type List<T> = Option<Box<Node<T>>>;

#[derive(Drop, Copy)]
struct Node<T> {
data: T,
next: List<T>,
}

#[generate_trait]
impl SimpleLinkedListImpl<T, +Drop<T>, +Copy<T>> of SimpleLinkedListTrait<T> {
fn new() -> SimpleLinkedList<T> {
SimpleLinkedList { head: Option::None, len: 0 }
}

fn is_empty(self: @SimpleLinkedList<T>) -> bool {
*self.len == 0
}

fn len(self: @SimpleLinkedList<T>) -> usize {
*self.len
}

fn push(ref self: SimpleLinkedList<T>, element: T) {
let node = BoxTrait::new(NodeTrait::new(element, self.head));
self.head = Option::Some(node);
self.len += 1;
}

fn pop(ref self: SimpleLinkedList<T>) -> Option<T> {
match self.head {
Option::None => Option::None,
Option::Some(node) => {
self.len -= 1;
let node = node.unbox();
self.head = node.next;
Option::Some(node.data)
}
}
}

fn peek(self: @SimpleLinkedList<T>) -> Option<@T> {
match *self.head {
Option::None => Option::None,
Option::Some(node) => Option::Some(@node.unbox().data)
}
}

#[must_use]
fn rev(self: SimpleLinkedList<T>) -> SimpleLinkedList<T> {
let mut self = self;
let mut rev_list = SimpleLinkedListTrait::new();
while let Option::Some(data) = self.pop() {
rev_list.push(data);
};
rev_list
}
}

impl ArrayIntoSimpleLinkedList<T, +Drop<T>, +Copy<T>> of Into<Array<T>, SimpleLinkedList<T>> {
#[must_use]
fn into(self: Array<T>) -> SimpleLinkedList<T> {
let mut self = self;
let mut list = SimpleLinkedListTrait::new();
while let Option::Some(data) = self.pop_front() {
list.push(data);
};
list
}
}

impl SimpleLinkedListIntoArray<T, +Drop<T>, +Copy<T>> of Into<SimpleLinkedList<T>, Array<T>> {
#[must_use]
fn into(self: SimpleLinkedList<T>) -> Array<T> {
let mut reversed = self.rev();
let mut arr: Array<T> = array![];
while let Option::Some(data) = reversed.pop() {
arr.append(data);
};
arr
}
}

#[generate_trait]
impl NodeImpl<T> of NodeTrait<T> {
fn new(element: T, next: List<T>) -> Node<T> {
Node { data: element, next, }
}
}

#[cfg(test)]
mod tests;
4 changes: 4 additions & 0 deletions exercises/practice/simple-linked-list/Scarb.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
[package]
name = "simple_linked_list"
version = "0.1.0"
edition = "2023_11"
51 changes: 51 additions & 0 deletions exercises/practice/simple-linked-list/src/lib.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
#[derive(Drop, Copy)]
struct SimpleLinkedList<T> {}

#[generate_trait]
impl SimpleLinkedListImpl<T, +Drop<T>, +Copy<T>> of SimpleLinkedListTrait<T> {
fn new() -> SimpleLinkedList<T> {
panic!()
}

fn is_empty(self: @SimpleLinkedList<T>) -> bool {
panic!()
}

fn len(self: @SimpleLinkedList<T>) -> usize {
panic!()
}

fn push(ref self: SimpleLinkedList<T>, element: T) {
panic!()
}

fn pop(ref self: SimpleLinkedList<T>) -> Option<T> {
panic!()
}

fn peek(self: @SimpleLinkedList<T>) -> Option<@T> {
panic!()
}

#[must_use]
fn rev(self: SimpleLinkedList<T>) -> SimpleLinkedList<T> {
panic!()
}
}

impl ArrayIntoSimpleLinkedList<T, +Drop<T>, +Copy<T>> of Into<Array<T>, SimpleLinkedList<T>> {
#[must_use]
fn into(self: Array<T>) -> SimpleLinkedList<T> {
panic!()
}
}

impl SimpleLinkedListIntoArray<T, +Drop<T>, +Copy<T>> of Into<SimpleLinkedList<T>, Array<T>> {
#[must_use]
fn into(self: SimpleLinkedList<T>) -> Array<T> {
panic!()
}
}

#[cfg(test)]
mod tests;
114 changes: 114 additions & 0 deletions exercises/practice/simple-linked-list/src/tests.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
use simple_linked_list::{SimpleLinkedListTrait, SimpleLinkedList};

#[test]
fn new_list_is_empty() {
let list = SimpleLinkedListTrait::<u32>::new();
assert_eq!(list.len(), 0, "list's length must be 0");
}

#[test]
fn push_increments_length() {
let mut list = SimpleLinkedListTrait::<u32>::new();
list.push(1);
assert_eq!(list.len(), 1, "list's length must be 1");
list.push(2);
assert_eq!(list.len(), 2, "list's length must be 2");
}

#[test]
fn pop_decrements_length() {
let mut list = SimpleLinkedListTrait::<u32>::new();
list.push(1);
list.push(2);
list.pop().unwrap();
assert_eq!(list.len(), 1, "list's length must be 1");
list.pop().unwrap();
assert_eq!(list.len(), 0, "list's length must be 0");
}

#[test]
fn pop_returns_head_element_and_removes_it() {
let mut list = SimpleLinkedListTrait::<u32>::new();
list.push(1);
list.push(2);
assert_eq!(list.pop(), Option::Some(2), "Element must be 2");
assert_eq!(list.pop(), Option::Some(1), "Element must be 1");
let none: Option<u32> = Option::None;
assert_eq!(list.pop(), none, "No element should be contained in list");
}

#[test]
fn peek_returns_reference_to_head_element_but_does_not_remove_it() {
let mut list = SimpleLinkedListTrait::<u32>::new();
let none: Option<@u32> = Option::None;
assert_eq!(list.peek(), none, "No element should be contained in list");
list.push(2);
assert_eq!(list.peek(), Option::Some(@2), "Element must be 2");
assert_eq!(list.peek(), Option::Some(@2), "Element must be still 2");
list.push(3);
assert_eq!(list.peek(), Option::Some(@3), "Head element is now 3");
assert_eq!(list.pop(), Option::Some(3), "Element must be 3");
assert_eq!(list.peek(), Option::Some(@2), "Head element is now 2");
assert_eq!(list.pop(), Option::Some(2), "Element must be 2");
assert_eq!(list.peek(), none, "No element should be contained in list");
}

#[test]
fn reverse() {
let mut list = SimpleLinkedListTrait::<u32>::new();
list.push(1);
list.push(2);
list.push(3);
let mut rev_list = list.rev();
assert_eq!(rev_list.pop(), Option::Some(1));
assert_eq!(rev_list.pop(), Option::Some(2));
assert_eq!(rev_list.pop(), Option::Some(3));
let none: Option<u32> = Option::None;
assert_eq!(rev_list.pop(), none);
}

#[test]
fn reverse_empty_list() {
let mut list = SimpleLinkedListTrait::<u32>::new();
let mut rev_list = list.rev();
let none: Option<u32> = Option::None;
assert_eq!(rev_list.pop(), none);
assert_eq!(rev_list.len(), 0);
}

#[test]
fn from_array() {
let mut array: Array<u32> = array![1, 2, 3, 4];
let mut list: SimpleLinkedList<u32> = array.into();
assert_eq!(list.pop(), Option::Some(4));
assert_eq!(list.pop(), Option::Some(3));
assert_eq!(list.pop(), Option::Some(2));
assert_eq!(list.pop(), Option::Some(1));
}

#[test]
fn from_empty_array_is_empty_list() {
let mut array: Array<u32> = array![];
let mut list: SimpleLinkedList<u32> = array.into();
let none: Option<u32> = Option::None;
assert_eq!(list.pop(), none);
assert_eq!(list.len(), 0);
}

#[test]
fn into_array() {
let mut list = SimpleLinkedListTrait::<u32>::new();
list.push(1);
list.push(2);
list.push(3);
list.push(4);
let list_as_arr: Array<u32> = list.into();
assert_eq!(array![1, 2, 3, 4], list_as_arr);
}

#[test]
fn empty_list_into_empty_array() {
let mut list = SimpleLinkedListTrait::<u32>::new();
let list_as_arr: Array<u32> = list.into();
assert_eq!(array![], list_as_arr);
}
Loading