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

implement linked-list exercise #372

Open
wants to merge 5 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
13 changes: 12 additions & 1 deletion config.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Copy link
Contributor

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.

Suggested change
"recursion"
"recursion"
]

},
{
"slug": "binary-search-tree",
"uuid": "ce05137f-d4e5-40f2-ae6b-6c43e00d458f",
Expand All @@ -461,4 +472,4 @@
]
}
]
}
}
Comment on lines -464 to +475
Copy link
Contributor

Choose a reason for hiding this comment

The 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 configlet fmt . isn't run at all. I'll open an issue to investigate.)

9 changes: 9 additions & 0 deletions exercises/linked-list/Makefile
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
29 changes: 29 additions & 0 deletions exercises/linked-list/README.md
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).
16 changes: 16 additions & 0 deletions exercises/linked-list/dune
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))))
Copy link
Contributor

Choose a reason for hiding this comment

The 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 configlet do.

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.

1 change: 1 addition & 0 deletions exercises/linked-list/dune-project
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
(lang dune 1.1)
48 changes: 48 additions & 0 deletions exercises/linked-list/example.ml
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
15 changes: 15 additions & 0 deletions exercises/linked-list/linked_list.ml
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"
15 changes: 15 additions & 0 deletions exercises/linked-list/linked_list.mli
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
97 changes: 97 additions & 0 deletions exercises/linked-list/test.ml
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)