aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--simple-linked-list/.exercism/metadata.json1
-rw-r--r--simple-linked-list/.gitignore8
-rw-r--r--simple-linked-list/Cargo.toml6
-rw-r--r--simple-linked-list/README.md137
-rw-r--r--simple-linked-list/src/lib.rs109
-rw-r--r--simple-linked-list/tests/simple-linked-list.rs118
6 files changed, 379 insertions, 0 deletions
diff --git a/simple-linked-list/.exercism/metadata.json b/simple-linked-list/.exercism/metadata.json
new file mode 100644
index 0000000..4c7e1a6
--- /dev/null
+++ b/simple-linked-list/.exercism/metadata.json
@@ -0,0 +1 @@
{"track":"rust","exercise":"simple-linked-list","id":"a0e2dbad46184d6a8ee961fff5e6c497","url":"https://exercism.io/my/solutions/a0e2dbad46184d6a8ee961fff5e6c497","handle":"dyamon","is_requester":true,"auto_approve":false} \ No newline at end of file
diff --git a/simple-linked-list/.gitignore b/simple-linked-list/.gitignore
new file mode 100644
index 0000000..db7f315
--- /dev/null
+++ b/simple-linked-list/.gitignore
@@ -0,0 +1,8 @@
1# Generated by Cargo
2# will have compiled files and executables
3/target/
4**/*.rs.bk
5
6# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
7# More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock
8Cargo.lock
diff --git a/simple-linked-list/Cargo.toml b/simple-linked-list/Cargo.toml
new file mode 100644
index 0000000..25a62e7
--- /dev/null
+++ b/simple-linked-list/Cargo.toml
@@ -0,0 +1,6 @@
1[package]
2edition = "2018"
3name = "simple_linked_list"
4version = "0.1.0"
5
6[dependencies]
diff --git a/simple-linked-list/README.md b/simple-linked-list/README.md
new file mode 100644
index 0000000..47178e8
--- /dev/null
+++ b/simple-linked-list/README.md
@@ -0,0 +1,137 @@
1# Simple Linked List
2
3Write a simple linked list implementation that uses Elements and a List.
4
5The linked list is a fundamental data structure in computer science,
6often used in the implementation of other data structures. They're
7pervasive in functional programming languages, such as Clojure, Erlang,
8or Haskell, but far less common in imperative languages such as Ruby or
9Python.
10
11The simplest kind of linked list is a singly linked list. Each element in the
12list contains data and a "next" field pointing to the next element in the list
13of elements.
14
15This variant of linked lists is often used to represent sequences or
16push-down stacks (also called a LIFO stack; Last In, First Out).
17
18As a first take, lets create a singly linked list to contain the range (1..10),
19and provide functions to reverse a linked list and convert to and from arrays.
20
21When implementing this in a language with built-in linked lists,
22implement your own abstract data type.
23
24## Implementation Hints
25
26Do not implement the struct `SimpleLinkedList` as a wrapper around a `Vec`. Instead, allocate nodes on the heap.
27This might be implemented as:
28```
29pub struct SimpleLinkedList<T> {
30 head: Option<Box<Node<T>>>,
31}
32```
33The `head` field points to the first element (Node) of this linked list.
34This implementation also requires a struct `Node` with the following fields:
35```
36struct Node<T> {
37 data: T,
38 next: Option<Box<Node<T>>>,
39}
40```
41`data` contains the stored data, and `next` points to the following node (if available) or None.
42
43### Why `Option<Box<Node<T>>>` and not just `Option<Node<T>>`?
44Try it on your own. You will get the following error.
45
46```
47| struct Node<T>
48| ^^^^^^^^^^^^^^ recursive type has infinite size
49...
50| next: Option<Node<T>>,
51| --------------------- recursive without indirection
52 ```
53
54 The problem is that at compile time the size of next must be known.
55 Since `next` is recursive ("a node has a node has a node..."), the compiler does not know how much memory is to be allocated.
56 In contrast, [Box](https://doc.rust-lang.org/std/boxed/) is a heap pointer with a defined size.
57
58
59## Rust Installation
60
61Refer to the [exercism help page][help-page] for Rust installation and learning
62resources.
63
64## Writing the Code
65
66Execute the tests with:
67
68```bash
69$ cargo test
70```
71
72All but the first test have been ignored. After you get the first test to
73pass, open the tests source file which is located in the `tests` directory
74and remove the `#[ignore]` flag from the next test and get the tests to pass
75again. Each separate test is a function with `#[test]` flag above it.
76Continue, until you pass every test.
77
78If you wish to run all ignored tests without editing the tests source file, use:
79
80```bash
81$ cargo test -- --ignored
82```
83
84To run a specific test, for example `some_test`, you can use:
85
86```bash
87$ cargo test some_test
88```
89
90If the specific test is ignored use:
91
92```bash
93$ cargo test some_test -- --ignored
94```
95
96To learn more about Rust tests refer to the [online test documentation][rust-tests]
97
98Make sure to read the [Modules][modules] chapter if you
99haven't already, it will help you with organizing your files.
100
101## Further improvements
102
103After you have solved the exercise, please consider using the additional utilities, described in the [installation guide](https://exercism.io/tracks/rust/installation), to further refine your final solution.
104
105To format your solution, inside the solution directory use
106
107```bash
108cargo fmt
109```
110
111To see, if your solution contains some common ineffective use cases, inside the solution directory use
112
113```bash
114cargo clippy --all-targets
115```
116
117## Submitting the solution
118
119Generally you should submit all files in which you implemented your solution (`src/lib.rs` in most cases). If you are using any external crates, please consider submitting the `Cargo.toml` file. This will make the review process faster and clearer.
120
121## Feedback, Issues, Pull Requests
122
123The [exercism/rust](https://github.com/exercism/rust) repository on GitHub is the home for all of the Rust exercises. If you have feedback about an exercise, or want to help implement new exercises, head over there and create an issue. Members of the rust track team are happy to help!
124
125If you want to know more about Exercism, take a look at the [contribution guide](https://github.com/exercism/docs/blob/master/contributing-to-language-tracks/README.md).
126
127[help-page]: https://exercism.io/tracks/rust/learning
128[modules]: https://doc.rust-lang.org/book/ch07-02-defining-modules-to-control-scope-and-privacy.html
129[cargo]: https://doc.rust-lang.org/book/ch14-00-more-about-cargo.html
130[rust-tests]: https://doc.rust-lang.org/book/ch11-02-running-tests.html
131
132## Source
133
134Inspired by 'Data Structures and Algorithms with Object-Oriented Design Patterns in Ruby', singly linked-lists. [https://web.archive.org/web/20160731005714/http://brpreiss.com/books/opus8/html/page96.html](https://web.archive.org/web/20160731005714/http://brpreiss.com/books/opus8/html/page96.html)
135
136## Submitting Incomplete Solutions
137It's possible to submit an incomplete solution so you can see how others have completed the exercise.
diff --git a/simple-linked-list/src/lib.rs b/simple-linked-list/src/lib.rs
new file mode 100644
index 0000000..75e82f5
--- /dev/null
+++ b/simple-linked-list/src/lib.rs
@@ -0,0 +1,109 @@
1use std::iter::FromIterator;
2
3#[derive(Debug)]
4struct Node<T> {
5 data: T,
6 next: Option<Box<Node<T>>>,
7}
8
9impl<T> Node<T> {
10 pub fn new(data: T, next: Option<Box<Node<T>>>) -> Self {
11 Node { data, next }
12 }
13
14 pub fn len(&self) -> usize {
15 1 + self.next.as_ref().map(|n| n.len()).unwrap_or(0)
16 }
17}
18
19#[derive(Debug)]
20pub struct SimpleLinkedList<T> {
21 head: Option<Box<Node<T>>>,
22}
23
24impl<T> SimpleLinkedList<T> {
25 pub fn new() -> Self {
26 SimpleLinkedList { head: None }
27 }
28
29 // You may be wondering why it's necessary to have is_empty()
30 // when it can easily be determined from len().
31 // It's good custom to have both because len() can be expensive for some types,
32 // whereas is_empty() is almost always cheap.
33 // (Also ask yourself whether len() is expensive for SimpleLinkedList)
34 pub fn is_empty(&self) -> bool {
35 self.head.is_none()
36 }
37
38 pub fn len(&self) -> usize {
39 self.head.as_ref().map(|n| n.len()).unwrap_or(0)
40 }
41
42 pub fn push(&mut self, _element: T) {
43 // let node = Box::new(Node::new(_element, self.head.take()));
44 // self.head.replace(node);
45 self.head = Some(Box::new(Node::new(_element, self.head.take())));
46 }
47
48 pub fn pop(&mut self) -> Option<T> {
49 self.head.take().map(|n| {
50 // n.next.map(|n1| self.head.replace(n1));
51 self.head = n.next;
52 n.data
53 })
54 }
55
56 pub fn peek(&self) -> Option<&T> {
57 self.head.as_ref().map(|n| &n.data)
58 }
59
60 pub fn rev(self) -> SimpleLinkedList<T> {
61 self.rev_aux(SimpleLinkedList::new())
62 }
63
64 pub fn rev_aux(mut self, mut acc: SimpleLinkedList<T>) -> SimpleLinkedList<T> {
65 if let Some(e) = self.pop() {
66 acc.push(e);
67 self.rev_aux(acc)
68 } else {
69 acc
70 }
71 }
72
73 fn into_aux(mut self, mut acc: Vec<T>) -> Vec<T> {
74 if let Some(e) = self.pop() {
75 acc.push(e);
76 self.into_aux(acc)
77 } else {
78 acc
79 }
80 }
81}
82
83impl<T> FromIterator<T> for SimpleLinkedList<T> {
84 fn from_iter<I: IntoIterator<Item = T>>(_iter: I) -> Self {
85 _iter
86 .into_iter()
87 .fold(SimpleLinkedList::new(), |mut acc, n| {
88 acc.push(n);
89 acc
90 })
91 }
92}
93
94// In general, it would be preferable to implement IntoIterator for SimpleLinkedList<T>
95// instead of implementing an explicit conversion to a vector. This is because, together,
96// FromIterator and IntoIterator enable conversion between arbitrary collections.
97// Given that implementation, converting to a vector is trivial:
98//
99// let vec: Vec<_> = simple_linked_list.into_iter().collect();
100//
101// The reason this exercise's API includes an explicit conversion to Vec<T> instead
102// of IntoIterator is that implementing that interface is fairly complicated, and
103// demands more of the student than we expect at this point in the track.
104
105impl<T> Into<Vec<T>> for SimpleLinkedList<T> {
106 fn into(self) -> Vec<T> {
107 self.rev().into_aux(Vec::new())
108 }
109}
diff --git a/simple-linked-list/tests/simple-linked-list.rs b/simple-linked-list/tests/simple-linked-list.rs
new file mode 100644
index 0000000..c89f8b5
--- /dev/null
+++ b/simple-linked-list/tests/simple-linked-list.rs
@@ -0,0 +1,118 @@
1use simple_linked_list::SimpleLinkedList;
2
3#[test]
4fn test_new_list_is_empty() {
5 let list: SimpleLinkedList<u32> = SimpleLinkedList::new();
6 assert_eq!(list.len(), 0, "list's length must be 0");
7}
8
9#[test]
10fn test_push_increments_length() {
11 let mut list: SimpleLinkedList<u32> = SimpleLinkedList::new();
12 list.push(1);
13 assert_eq!(list.len(), 1, "list's length must be 1");
14 list.push(2);
15 assert_eq!(list.len(), 2, "list's length must be 2");
16}
17
18#[test]
19fn test_pop_decrements_length() {
20 let mut list: SimpleLinkedList<u32> = SimpleLinkedList::new();
21 list.push(1);
22 list.push(2);
23 list.pop();
24 assert_eq!(list.len(), 1, "list's length must be 1");
25 list.pop();
26 assert_eq!(list.len(), 0, "list's length must be 0");
27}
28
29#[test]
30fn test_is_empty() {
31 let mut list: SimpleLinkedList<u32> = SimpleLinkedList::new();
32 assert!(list.is_empty(), "List wasn't empty on creation");
33 for inserts in 0..100 {
34 for i in 0..inserts {
35 list.push(i);
36 assert!(
37 !list.is_empty(),
38 "List was empty after having inserted {}/{} elements",
39 i,
40 inserts
41 );
42 }
43 for i in 0..inserts {
44 assert!(
45 !list.is_empty(),
46 "List was empty before removing {}/{} elements",
47 i,
48 inserts
49 );
50 list.pop();
51 }
52 assert!(
53 list.is_empty(),
54 "List wasn't empty after having removed {} elements",
55 inserts
56 );
57 }
58}
59
60#[test]
61fn test_pop_returns_head_element_and_removes_it() {
62 let mut list: SimpleLinkedList<u32> = SimpleLinkedList::new();
63 list.push(1);
64 list.push(2);
65 assert_eq!(list.pop(), Some(2), "Element must be 2");
66 assert_eq!(list.pop(), Some(1), "Element must be 1");
67 assert_eq!(list.pop(), None, "No element should be contained in list");
68}
69
70#[test]
71fn test_peek_returns_reference_to_head_element_but_does_not_remove_it() {
72 let mut list: SimpleLinkedList<u32> = SimpleLinkedList::new();
73 assert_eq!(list.peek(), None, "No element should be contained in list");
74 list.push(2);
75 assert_eq!(list.peek(), Some(&2), "Element must be 2");
76 assert_eq!(list.peek(), Some(&2), "Element must be still 2");
77 list.push(3);
78 assert_eq!(list.peek(), Some(&3), "Head element is now 3");
79 assert_eq!(list.pop(), Some(3), "Element must be 3");
80 assert_eq!(list.peek(), Some(&2), "Head element is now 2");
81 assert_eq!(list.pop(), Some(2), "Element must be 2");
82 assert_eq!(list.peek(), None, "No element should be contained in list");
83}
84
85#[test]
86fn test_from_slice() {
87 let mut array = vec!["1", "2", "3", "4"];
88 let mut list: SimpleLinkedList<_> = array.drain(..).collect();
89 assert_eq!(list.pop(), Some("4"));
90 assert_eq!(list.pop(), Some("3"));
91 assert_eq!(list.pop(), Some("2"));
92 assert_eq!(list.pop(), Some("1"));
93}
94
95#[test]
96fn test_reverse() {
97 let mut list: SimpleLinkedList<u32> = SimpleLinkedList::new();
98 list.push(1);
99 list.push(2);
100 list.push(3);
101 let mut rev_list = list.rev();
102 assert_eq!(rev_list.pop(), Some(1));
103 assert_eq!(rev_list.pop(), Some(2));
104 assert_eq!(rev_list.pop(), Some(3));
105 assert_eq!(rev_list.pop(), None);
106}
107
108#[test]
109fn test_into_vector() {
110 let mut v = Vec::new();
111 let mut s = SimpleLinkedList::new();
112 for i in 1..4 {
113 v.push(i);
114 s.push(i);
115 }
116 let s_as_vec: Vec<i32> = s.into();
117 assert_eq!(v, s_as_vec);
118}