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

Cleanup #19

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
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
21 changes: 8 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
* [Implementing insert_before()](#implementing-insert_before)
* [Conclusion](#conclusion)


## Introduction

In a 2016 [TED interview][ted] (14:10) Linus Torvalds speaks about what he
Expand All @@ -38,7 +37,6 @@ The next two sections look at the technical approach in detail and demonstrate
how and why the indirect addressing approach is so neat. The last section
extends the solution from item deletion to insertion.


## The code

The basic data structure for a singly linked list of integers is shown in
Expand All @@ -50,7 +48,7 @@ Figure 1.
<b>Figure 1</b>: Singly linked list of integers.
</p>

Numbers are arbitrarily chosen integer values and arrows indicate pointers.
Numbers are arbitrarily chosen, integer values and arrows indicate pointers.
`head` is a pointer of type `list_item *` and each of the boxes
is an instance of an `list_item` struct, each with a member variable (called
`next` in the code) of type `list_item *` that points to the next item.
Expand All @@ -68,9 +66,9 @@ struct list {
struct list_item *head;
};
typedef struct list list;

```
We also include a (minimal) API:

We also include a minimal API:

```c
/* The textbook version */
Expand Down Expand Up @@ -138,14 +136,13 @@ i.e. the address of the `next` element in the current `list_item`.
When the pointer to the list item `*p` equals `target`, we exit the search
loop and remove the item from the list.


## How does it work?

The key insight is that using an indirect pointer `p` has two conceptual
benefits:

1. It allows us to interpret the linked list in a way that makes the `head`
pointer an integral part the data structure. This eliminates the need
pointer an integral part the data structure. This eliminates the need
for a special case to remove the first item.
2. It also allows us to evaluate the condition of the `while` loop without
having to let go of the pointer that points to `target`. This allows us to
Expand Down Expand Up @@ -223,28 +220,26 @@ First, let's add the following declaration to the list API in `list.h`:
void insert_before(list *l, list_item *before, list_item *item);
```

The function will take a pointer to a list `l`, a pointer `before` to an
The function will take a pointer to a list `l`, a pointer `before` to an
item in that list and a pointer to a new list item `item` that the function
will insert before `before`.

### Quick refactor

Before we move on, we refactor the search loop into a separate
function
function:

```c

static inline list_item **find_indirect(list *l, list_item *target)
{
list_item **p = &l->head;
while (*p != target)
p = &(*p)->next;
return p;
}

```

and use that function in `remove_elegant()` like so
and use that function in `remove_elegant()` like so:

```c
void remove_elegant(list *l, list_item *target)
Expand Down Expand Up @@ -273,14 +268,14 @@ will be inserted at the beginning of the list, if `before` is `NULL` or invalid
(i.e. the item does not exist in `l`), the new item will be appended at the
end.


## Conclusion

The premise of the more elegant solution for item deletion is a single, simple
change: using an indirect `list_item **` pointer to iterate over the pointers
to the list items. Everything else flows from there: there is no need for a
special case or branching and a single iterator is sufficient to find and
remove the target item.

It also turns out that the same approach provides an elegant solution for item
insertion in general and for insertion *before* an existing item in particular.

Expand Down
2 changes: 1 addition & 1 deletion src/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ test: test_list
list.o: list.c list.h
$(CC) $(CFLAGS) -c $< -o $@

test_list: test_list.c
test_list: test_list.c list.c list.h
$(CC) $(CFLAGS) $^ -o $@

clean:
Expand Down
2 changes: 1 addition & 1 deletion src/test_list.c
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#include "minunit.h"
#include <stdio.h>
#include <stdlib.h>
#include "list.c"
#include "list.h"

#define N 1000

Expand Down