Skip to content

Commit 32e76e9

Browse files
committed
simple-linked-list, doubly-linked-list: Add is_empty
https://rust-lang.github.io/rust-clippy/master/index.html#len_without_is_empty This alleges that for certain types, len() may be expensive, but is_empty() is likely to always be cheap, and therefore it's good custom to have both. I find this sufficiently persuasive. On the principle that the track instill good habits in students, we will add and test such a method. I understand that for the example solutions, len() is actually cheap. In addition, I understand that for a list it's always possible to be able to cheaply determine the `len()` simply by keeping an extra field that gets updated on every insert. Nevertheless I think it's a good idea anyway. Helps address #1011 Helps address #1012
1 parent ea5ea52 commit 32e76e9

File tree

8 files changed

+81
-4
lines changed

8 files changed

+81
-4
lines changed

exercises/doubly-linked-list/.meta/description.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ private functions.
3333

3434
Implement the functionality for adding and removing elements (pushing and popping)
3535
at the front and back. This is enough to use the list as a double-ended queue.
36-
Also implement the `len` function.
36+
Also implement the `len` and `is_empty` functions.
3737

3838
In the finished implementation, all modifications of the list should be done through the cursor struct
3939
to minimize duplication. The `push_*` and `pop_*` methods on `LinkedList`

exercises/doubly-linked-list/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ private functions.
3535

3636
Implement the functionality for adding and removing elements (pushing and popping)
3737
at the front and back. This is enough to use the list as a double-ended queue.
38-
Also implement the `len` function.
38+
Also implement the `len` and `is_empty` functions.
3939

4040
In the finished implementation, all modifications of the list should be done through the cursor struct
4141
to minimize duplication. The `push_*` and `pop_*` methods on `LinkedList`

exercises/doubly-linked-list/example.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,10 @@ impl<T> LinkedList<T> {
7777
}
7878
}
7979

80+
pub fn is_empty(&self) -> bool {
81+
self.len == 0
82+
}
83+
8084
pub fn len(&self) -> usize {
8185
self.len
8286
}

exercises/doubly-linked-list/src/lib.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,15 @@ impl<T> LinkedList<T> {
1414
unimplemented!()
1515
}
1616

17+
// You may be wondering why it's necessary to have is_empty()
18+
// when it can easily be determined from len().
19+
// It's good custom to have both because len() can be expensive for some types,
20+
// whereas is_empty() is almost always cheap.
21+
// (Also ask yourself whether len() is expensive for LinkedList)
22+
pub fn is_empty(&self) -> bool {
23+
unimplemented!()
24+
}
25+
1726
pub fn len(&self) -> usize {
1827
unimplemented!()
1928
}

exercises/doubly-linked-list/tests/doubly-linked-list.rs

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ fn is_generic() {
1515
fn basics_empty_list() {
1616
let list: LinkedList<i32> = LinkedList::new();
1717
assert_eq!(list.len(), 0);
18+
assert!(list.is_empty());
1819
}
1920

2021
// push / pop at back ————————————————————————————————————————
@@ -25,7 +26,11 @@ fn basics_single_element_back() {
2526
list.push_back(5);
2627

2728
assert_eq!(list.len(), 1);
29+
assert!(!list.is_empty());
30+
2831
assert_eq!(list.pop_back(), Some(5));
32+
33+
assert!(list.is_empty());
2934
}
3035

3136
#[test]
@@ -34,13 +39,15 @@ fn basics_push_pop_at_back() {
3439
let mut list: LinkedList<i32> = LinkedList::new();
3540
for i in 0..10 {
3641
list.push_back(i);
42+
assert!(!list.is_empty());
3743
}
3844
assert_eq!(list.len(), 10);
39-
4045
for i in (0..10).rev() {
46+
assert!(!list.is_empty());
4147
assert_eq!(i, list.pop_back().unwrap());
4248
}
4349
assert_eq!(list.len(), 0);
50+
assert!(list.is_empty());
4451
}
4552

4653
// push / pop at front ———————————————————————————————————————
@@ -51,7 +58,11 @@ fn basics_single_element_front() {
5158
list.push_front(5);
5259

5360
assert_eq!(list.len(), 1);
61+
assert!(!list.is_empty());
62+
5463
assert_eq!(list.pop_front(), Some(5));
64+
65+
assert!(list.is_empty());
5566
}
5667

5768
#[test]
@@ -60,13 +71,15 @@ fn basics_push_pop_at_front() {
6071
let mut list: LinkedList<i32> = LinkedList::new();
6172
for i in 0..10 {
6273
list.push_front(i);
74+
assert!(!list.is_empty());
6375
}
6476
assert_eq!(list.len(), 10);
65-
6677
for i in (0..10).rev() {
78+
assert!(!list.is_empty());
6779
assert_eq!(i, list.pop_front().unwrap());
6880
}
6981
assert_eq!(list.len(), 0);
82+
assert!(list.is_empty());
7083
}
7184

7285
// push / pop at mixed sides —————————————————————————————————
@@ -76,10 +89,13 @@ fn basics_push_front_pop_back() {
7689
let mut list: LinkedList<i32> = LinkedList::new();
7790
for i in 0..10 {
7891
list.push_front(i);
92+
assert!(!list.is_empty());
7993
}
8094
for i in 0..10 {
95+
assert!(!list.is_empty());
8196
assert_eq!(i, list.pop_back().unwrap());
8297
}
98+
assert!(list.is_empty());
8399
}
84100

85101
#[test]
@@ -88,10 +104,13 @@ fn basics_push_back_pop_front() {
88104
let mut list: LinkedList<i32> = LinkedList::new();
89105
for i in 0..10 {
90106
list.push_back(i);
107+
assert!(!list.is_empty());
91108
}
92109
for i in 0..10 {
110+
assert!(!list.is_empty());
93111
assert_eq!(i, list.pop_front().unwrap());
94112
}
113+
assert!(list.is_empty());
95114
}
96115

97116
// ———————————————————————————————————————————————————————————

exercises/simple-linked-list/example.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@ impl<T> SimpleLinkedList<T> {
1515
SimpleLinkedList { head: None, len: 0 }
1616
}
1717

18+
pub fn is_empty(&self) -> bool {
19+
self.len == 0
20+
}
21+
1822
pub fn len(&self) -> usize {
1923
self.len
2024
}

exercises/simple-linked-list/src/lib.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,15 @@ impl<T> SimpleLinkedList<T> {
1111
unimplemented!()
1212
}
1313

14+
// You may be wondering why it's necessary to have is_empty()
15+
// when it can easily be determined from len().
16+
// It's good custom to have both because len() can be expensive for some types,
17+
// whereas is_empty() is almost always cheap.
18+
// (Also ask yourself whether len() is expensive for SimpleLinkedList)
19+
pub fn is_empty(&self) -> bool {
20+
unimplemented!()
21+
}
22+
1423
pub fn len(&self) -> usize {
1524
unimplemented!()
1625
}

exercises/simple-linked-list/tests/simple-linked-list.rs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,38 @@ fn test_pop_decrements_length() {
2828
assert_eq!(list.len(), 0, "list's length must be 0");
2929
}
3030

31+
#[test]
32+
#[ignore]
33+
fn test_is_empty() {
34+
let mut list: SimpleLinkedList<u32> = SimpleLinkedList::new();
35+
assert!(list.is_empty(), "List wasn't empty on creation");
36+
for inserts in 0..100 {
37+
for i in 0..inserts {
38+
list.push(i);
39+
assert!(
40+
!list.is_empty(),
41+
"List was empty after having inserted {}/{} elements",
42+
i,
43+
inserts
44+
);
45+
}
46+
for i in 0..inserts {
47+
assert!(
48+
!list.is_empty(),
49+
"List was empty before removing {}/{} elements",
50+
i,
51+
inserts
52+
);
53+
list.pop();
54+
}
55+
assert!(
56+
list.is_empty(),
57+
"List wasn't empty after having removed {} elements",
58+
inserts
59+
);
60+
}
61+
}
62+
3163
#[test]
3264
#[ignore]
3365
fn test_pop_returns_head_element_and_removes_it() {

0 commit comments

Comments
 (0)