-
-
Notifications
You must be signed in to change notification settings - Fork 51
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
implement linked-list exercise #372
base: main
Are you sure you want to change the base?
Changes from all commits
5fbe121
d50f903
6cc34fc
5ef1366
073982c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -448,6 +448,17 @@ | |
"topics": null, | ||
"deprecated": true | ||
}, | ||
{ | ||
"slug": "linked-list", | ||
"uuid": "840f4011-029a-4f5c-970f-f8e585cd4fdf", | ||
"core": false, | ||
"unlocked_by": "null", | ||
"difficulty": 8, | ||
"topics": [ | ||
"data_structures", | ||
"lists", | ||
"recursion" | ||
}, | ||
{ | ||
"slug": "binary-search-tree", | ||
"uuid": "ce05137f-d4e5-40f2-ae6b-6c43e00d458f", | ||
|
@@ -461,4 +472,4 @@ | |
] | ||
} | ||
] | ||
} | ||
} | ||
Comment on lines
-464
to
+475
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This change is unnecessary to the subject of the PR. (Also, a CI check should probably have failed here. Considering how CI didn't catch a syntax error, perhaps |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
default: clean test | ||
|
||
test: | ||
dune runtest | ||
|
||
clean: | ||
dune clean | ||
|
||
.PHONY: clean |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
# Linked List | ||
|
||
Implement a doubly linked list. | ||
|
||
Like an array, a linked list is a simple linear data structure. Several | ||
common data types can be implemented using linked lists, like queues, | ||
stacks, and associative arrays. | ||
|
||
A linked list is a collection of data elements called *nodes*. In a | ||
*singly linked list* each node holds a value and a link to the next node. | ||
In a *doubly linked list* each node also holds a link to the previous | ||
node. | ||
|
||
You will write an implementation of a doubly linked list. Implement a | ||
Node to hold a value and pointers to the next and previous nodes. Then | ||
implement a List which holds references to the first and last node and | ||
offers an array-like interface for adding and removing items: | ||
|
||
* `push` (*insert value at back*); | ||
* `pop` (*remove value at back*); | ||
* `shift` (*remove value at front*). | ||
* `unshift` (*insert value at front*); | ||
* `count` (*count elements*); | ||
|
||
To keep your implementation simple, the tests will not cover error | ||
conditions. Specifically: `pop` or `shift` will never be called on an | ||
empty list. | ||
|
||
If you want to know more about linked lists, check [Wikipedia](https://en.wikipedia.org/wiki/Linked_list). |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
(executable | ||
(name test) | ||
(libraries base oUnit)) | ||
|
||
(alias | ||
(name runtest) | ||
(deps (:x test.exe)) | ||
(action (run %{x}))) | ||
|
||
(alias | ||
(name buildtest) | ||
(deps (:x test.exe))) | ||
|
||
(env | ||
(dev | ||
(flags (:standard -warn-error -A)))) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I notice that most (but not all of) the files you generate don't have line termination and the files generated by I don't have my development computer at hand, so I can't say if we are consistent about this across the track. The easiest consistency we could aim for is across single exercises, which I think we should aim for either way. Since README.md is forced to have line endings, I think this and other files should have, too. Feel free to object and argue otherwise. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
(lang dune 1.1) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
open Base | ||
|
||
type 'a node = | ||
| Empty | ||
| Node of { value: 'a; mutable prev: 'a node; mutable next: 'a node} | ||
and 'a linked_list = { mutable first: 'a node; mutable last:'a node} | ||
|
||
let empty () = {first = Empty; last = Empty} | ||
|
||
let push v l = | ||
let elt = Node { value = v; prev = l.last; next = Empty } in ( | ||
match l.last with | ||
| Empty -> l.first <- elt | ||
| Node n -> n.next <- elt | ||
); | ||
l.last <- elt | ||
|
||
let pop = function | ||
| {last = Empty; _} -> failwith "empty list!" | ||
| {last = Node {value; prev; _}; _} as l -> ( | ||
match prev with | ||
| Empty -> (l.last <- Empty; l.first <- Empty) | ||
| Node n -> (l.last <- prev; n.next <- Empty) | ||
); | ||
value | ||
|
||
let shift = function | ||
| {first = Empty; _} -> failwith "empty list!" | ||
| {first = Node {value; next; _}; _} as l -> ( | ||
match next with | ||
| Empty -> (l.first <- Empty; l.last <- Empty) | ||
| Node n -> (l.first <- next; n.prev <- Empty) | ||
); | ||
value | ||
|
||
let unshift v l = | ||
let elt = Node { value = v; prev = Empty; next = l.first } in ( | ||
match l.first with | ||
| Empty -> l.last <- elt | ||
| Node n -> n.prev <- elt | ||
); | ||
l.first <- elt | ||
|
||
let count l = | ||
let rec count_node acc = function | ||
| Empty -> acc | ||
| Node {next = n; _} -> count_node (1 + acc) n | ||
in count_node 0 l.first |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
open Base | ||
|
||
type 'a linked_list | ||
|
||
let empty _ = failwith "'empty' is missing" | ||
|
||
let push _ _ = failwith "'push' is missing" | ||
|
||
let pop _ = failwith "'pop' is missing" | ||
|
||
let shift _ = failwith "'shift' is missing" | ||
|
||
let unshift _ = failwith "'unshift' is missing" | ||
|
||
let count _ = failwith "'count' is missing" |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
open Base | ||
|
||
type 'a linked_list | ||
|
||
val empty : unit -> 'a linked_list | ||
|
||
val push : 'a -> 'a linked_list -> unit | ||
|
||
val pop : 'a linked_list -> 'a | ||
|
||
val shift : 'a linked_list -> 'a | ||
|
||
val unshift : 'a -> 'a linked_list -> unit | ||
|
||
val count : 'a linked_list -> int |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
open Base | ||
open OUnit2 | ||
open Linked_list | ||
|
||
let ae exp got _test_ctxt = | ||
assert_equal ~printer:Int.to_string exp got | ||
|
||
let test1 = | ||
let l = empty () in | ||
push 10 l; | ||
push 20 l; | ||
let v1 = pop l in | ||
let v2 = pop l in | ||
["add/extract elements to the end of the list with push/pop" >:: ae 20 v1; | ||
"add/extract elements to the end of the list with push/pop" >:: ae 10 v2] | ||
|
||
let test2 = | ||
let l = empty () in | ||
push 10 l; | ||
push 20 l; | ||
let v1 = shift l in | ||
let v2 = shift l in | ||
["extract elements from the beginning of the list with shift" >:: ae 10 v1; | ||
"extract elements from the beginning of the list with shift" >:: ae 20 v2] | ||
|
||
let test3 = | ||
let l = empty () in | ||
unshift 10 l; | ||
unshift 20 l; | ||
let v1 = shift l in | ||
let v2 = shift l in | ||
["add/extract elements from the beginning of the list with unshift/shift" >:: ae 20 v1; | ||
"add/extract elements from the beginning of the list with unshift/shift" >:: ae 10 v2] | ||
|
||
let test4 = | ||
let l = empty () in | ||
unshift 10 l; | ||
unshift 20 l; | ||
let v1 = pop l in | ||
let v2 = pop l in | ||
["add/extract elements from the beginning of the list with unshift/shift" >:: ae 10 v1; | ||
"add/extract elements from the beginning of the list with unshift/shift" >:: ae 20 v2] | ||
|
||
let test5 = | ||
let l = empty () in | ||
push 10 l; | ||
push 20 l; | ||
let v1 = pop l in | ||
push 30 l; | ||
let v2 = shift l in | ||
unshift 40 l; | ||
push 50 l; | ||
let v3 = shift l in | ||
let v4 = pop l in | ||
let v5 = shift l in | ||
["example" >:: ae 20 v1; | ||
"example" >:: ae 10 v2; | ||
"example" >:: ae 40 v3; | ||
"example" >:: ae 50 v4; | ||
"example" >:: ae 30 v5] | ||
|
||
let test6 = | ||
let l = empty () in | ||
let v1 = count l in | ||
push 10 l; | ||
let v2 = count l in | ||
push 20 l; | ||
let v3 = count l in | ||
["can count its elements" >:: ae 0 v1; | ||
"can count its elements" >:: ae 1 v2; | ||
"can count its elements" >:: ae 2 v3] | ||
|
||
let test7 = | ||
let l = empty () in | ||
push 10 l; | ||
let _ = pop l in | ||
unshift 20 l; | ||
let v1 = count l in | ||
let v2 = pop l in | ||
["sets head/tail after popping last element" >:: ae 1 v1; | ||
"sets head/tail after popping last element" >:: ae 20 v2] | ||
|
||
let test8 = | ||
let l = empty () in | ||
unshift 10 l; | ||
let _ = shift l in | ||
push 20 l; | ||
let v1 = count l in | ||
let v2 = shift l in | ||
["sets head/tail after shifting last element" >:: ae 1 v1; | ||
"sets head/tail after shifting last element" >:: ae 20 v2] | ||
|
||
let tests = | ||
test1 @ test2 @ test3 @ test4 @ test5 @ test6 @ test7 @ test8 | ||
|
||
let () = | ||
run_test_tt_main ("linked-list tests" >::: tests) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a syntax error. Our CI should definitely have caught this.