diff --git a/config.json b/config.json index 292ca14978..e28a613a58 100644 --- a/config.json +++ b/config.json @@ -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 @@ ] } ] -} +} \ No newline at end of file diff --git a/exercises/linked-list/Makefile b/exercises/linked-list/Makefile new file mode 100644 index 0000000000..b71d6af2de --- /dev/null +++ b/exercises/linked-list/Makefile @@ -0,0 +1,9 @@ +default: clean test + +test: + dune runtest + +clean: + dune clean + +.PHONY: clean diff --git a/exercises/linked-list/README.md b/exercises/linked-list/README.md new file mode 100644 index 0000000000..137ef7aa39 --- /dev/null +++ b/exercises/linked-list/README.md @@ -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). \ No newline at end of file diff --git a/exercises/linked-list/dune b/exercises/linked-list/dune new file mode 100644 index 0000000000..3d0b594af7 --- /dev/null +++ b/exercises/linked-list/dune @@ -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)))) \ No newline at end of file diff --git a/exercises/linked-list/dune-project b/exercises/linked-list/dune-project new file mode 100644 index 0000000000..7655de0773 --- /dev/null +++ b/exercises/linked-list/dune-project @@ -0,0 +1 @@ +(lang dune 1.1) diff --git a/exercises/linked-list/example.ml b/exercises/linked-list/example.ml new file mode 100644 index 0000000000..67b734add4 --- /dev/null +++ b/exercises/linked-list/example.ml @@ -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 \ No newline at end of file diff --git a/exercises/linked-list/linked_list.ml b/exercises/linked-list/linked_list.ml new file mode 100644 index 0000000000..e40fd6f129 --- /dev/null +++ b/exercises/linked-list/linked_list.ml @@ -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" diff --git a/exercises/linked-list/linked_list.mli b/exercises/linked-list/linked_list.mli new file mode 100644 index 0000000000..976f151c6c --- /dev/null +++ b/exercises/linked-list/linked_list.mli @@ -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 \ No newline at end of file diff --git a/exercises/linked-list/test.ml b/exercises/linked-list/test.ml new file mode 100644 index 0000000000..0b8cd6f30b --- /dev/null +++ b/exercises/linked-list/test.ml @@ -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)