Skip to content

Commit 7f45dad

Browse files
authored
Add linked-list exercise (exercism#113)
1 parent 589e904 commit 7f45dad

File tree

16 files changed

+948
-0
lines changed

16 files changed

+948
-0
lines changed

config.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -642,6 +642,14 @@
642642
"prerequisites": [],
643643
"difficulty": 8
644644
},
645+
{
646+
"slug": "linked-list",
647+
"name": "Linked List",
648+
"uuid": "55d105a8-0f79-47b3-8599-53136584aa60",
649+
"practices": [],
650+
"prerequisites": [],
651+
"difficulty": 8
652+
},
645653
{
646654
"slug": "rational-numbers",
647655
"name": "Rational Numbers",
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# Instructions append
2+
3+
## Stateful data structures
4+
5+
In functional languages, such as Lean, most values are immutable by design.
6+
This means updates are usually made by producing a new value.
7+
Both the old and the new value coexist and may be referenced and used.
8+
9+
However, there are many situations where a _stateful_ value is desired, or even necessary.
10+
A stateful value is updated by mutation, _replacing_ the old value rather than creating a new one.
11+
After a mutation, only the updated value remains accessible.
12+
13+
Note that mutations can break some guarantees of the language.
14+
A pure function must always return the same result for the same set of inputs, but if an input may change over time, then the function may return different results even when called with the same arguments.
15+
Similarly, if a function can produce external changes other than returning a result, or can read from sources other than its parameters, then two calls to the same function may have different and sometimes unpredictable outcomes.
16+
17+
To handle such stateful behavior, Lean, like other functional languages, requires that effectful functions be wrapped in a special kind of structure that distinguishes them from pure functions.
18+
These structures are called _monads_.
19+
20+
It is important to note that, although every stateful value lives inside some effectful monad, not every monad represents mutable state.
21+
Notable examples of "pure" monads are `Option`, `Except` and `Id`.
22+
23+
In this exercise, you are going to define a stateful data structure called `LinkedList`.
24+
Instances of this data structure are updated by mutation.
25+
26+
To support this, all functions are wrapped inside the `ST` monad.
27+
In this monad, mutable state is the only side effect, and mutable references cannot escape.
28+
For more information, you can check the [reference][reference].
29+
30+
[reference]: https://lean-lang.org/doc/reference/latest/IO/Mutable-References/#mutable-st-references
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# Instructions
2+
3+
Your team has decided to use a doubly linked list to represent each train route in the schedule.
4+
Each station along the train's route will be represented by a node in the linked list.
5+
6+
You don't need to worry about arrival and departure times at the stations.
7+
Each station will simply be represented by a number.
8+
9+
Routes can be extended, adding stations to the beginning or end of a route.
10+
They can also be shortened by removing stations from the beginning or the end of a route.
11+
12+
Sometimes a station gets closed down, and in that case the station needs to be removed from the route, even if it is not at the beginning or end of the route.
13+
14+
The size of a route is measured not by how far the train travels, but by how many stations it stops at.
15+
16+
~~~~exercism/note
17+
The linked list is a fundamental data structure in computer science, often used in the implementation of other data structures.
18+
As the name suggests, it is a list of nodes that are linked together.
19+
It is a list of "nodes", where each node links to its neighbor or neighbors.
20+
In a **singly linked list** each node links only to the node that follows it.
21+
In a **doubly linked list** each node links to both the node that comes before, as well as the node that comes after.
22+
23+
If you want to dig deeper into linked lists, check out [this article][intro-linked-list] that explains it using nice drawings.
24+
25+
[intro-linked-list]: https://medium.com/basecs/whats-a-linked-list-anyway-part-1-d8b7e6508b9d
26+
~~~~
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# Introduction
2+
3+
You are working on a project to develop a train scheduling system for a busy railway network.
4+
5+
You've been asked to develop a prototype for the train routes in the scheduling system.
6+
Each route consists of a sequence of train stations that a given train stops at.
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
import Std
2+
open Std
3+
4+
namespace LinkedList
5+
6+
variable {α σ : Type} [BEq α] [Inhabited α]
7+
8+
structure Node (α : Type) where
9+
prev : Option Nat
10+
value : α
11+
next : Option Nat
12+
deriving Inhabited
13+
14+
structure LinkedList σ α where
15+
nodes : ST.Ref σ (Array (Node α))
16+
free : ST.Ref σ (Array Nat)
17+
head : ST.Ref σ (Option Nat)
18+
tail : ST.Ref σ (Option Nat)
19+
length : ST.Ref σ Nat
20+
21+
def LinkedList.empty : ST σ (LinkedList σ α) := do
22+
return {
23+
nodes := ← ST.mkRef #[],
24+
free := ← ST.mkRef #[],
25+
head := ← ST.mkRef none,
26+
tail := ← ST.mkRef none,
27+
length := ← ST.mkRef 0
28+
}
29+
30+
private def buildNode (value : α) (list : LinkedList σ α) : ST σ Nat := do
31+
let node : Node α := { prev := none, value := value, next := none}
32+
match (← list.free.get).back? with
33+
| none =>
34+
let id := (← list.nodes.get).size
35+
list.nodes.modify (·.push node)
36+
return id
37+
| some id =>
38+
list.nodes.modify (·.set! id node)
39+
list.free.modify (·.pop)
40+
return id
41+
42+
private def LinkedList.unlinkPrev (node : Node α) (list : LinkedList σ α) : ST σ Unit := do
43+
match node.prev with
44+
| some p =>
45+
list.nodes.modify (·.modify p (λ pn => { pn with next := node.next }))
46+
| none =>
47+
list.head.set node.next
48+
49+
private def LinkedList.unlinkNext (node : Node α) (list : LinkedList σ α) : ST σ Unit := do
50+
match node.next with
51+
| some n =>
52+
list.nodes.modify (·.modify n (λ np => { np with prev := node.prev }))
53+
| none =>
54+
list.tail.set node.prev
55+
56+
private def LinkedList.unlink (node : Node α) (list : LinkedList σ α) : ST σ Unit := do
57+
list.unlinkPrev node
58+
list.unlinkNext node
59+
60+
def LinkedList.count (list : LinkedList σ α) : ST σ Nat :=
61+
list.length.get
62+
63+
def LinkedList.push (value : α) (list : LinkedList σ α) : ST σ Unit := do
64+
let id ← buildNode value list
65+
match ← list.tail.get with
66+
| none =>
67+
list.head.set (some id)
68+
list.tail.set (some id)
69+
list.length.modify (· + 1)
70+
| some t =>
71+
list.nodes.modify (λ ns =>
72+
ns.modify t (λ n => { n with next := some id })
73+
|>.modify id (λ n => { n with prev := some t })
74+
)
75+
list.tail.set (some id)
76+
list.length.modify (· + 1)
77+
78+
def LinkedList.unshift (value : α) (list : LinkedList σ α) : ST σ Unit := do
79+
let id ← buildNode value list
80+
match ← list.head.get with
81+
| none =>
82+
list.head.set (some id)
83+
list.tail.set (some id)
84+
list.length.modify (· + 1)
85+
| some h =>
86+
list.nodes.modify (λ ns =>
87+
ns.modify h (λ n => { n with prev := some id })
88+
|>.modify id (λ n => { n with next := some h })
89+
)
90+
list.head.set (some id)
91+
list.length.modify (· + 1)
92+
93+
def LinkedList.pop (list : LinkedList σ α) : ST σ (Option α) := do
94+
match (← list.length.get) with
95+
| 0 => return none
96+
| n + 1 =>
97+
let id := (← list.tail.get).get!
98+
let node := (← list.nodes.get)[id]!
99+
list.unlink node
100+
list.length.set n
101+
list.free.modify (·.push id)
102+
return node.value
103+
104+
def LinkedList.shift (list : LinkedList σ α) : ST σ (Option α) := do
105+
match (← list.length.get) with
106+
| 0 => return none
107+
| n + 1 =>
108+
let id := (← list.head.get).get!
109+
let node := (← list.nodes.get)[id]!
110+
list.unlink node
111+
list.length.set n
112+
list.free.modify (·.push id)
113+
return node.value
114+
115+
def LinkedList.delete (value : α) (list : LinkedList σ α) : ST σ Unit := do
116+
let mut crtId ← list.head.get
117+
while crtId.isSome do
118+
let id := crtId.get!
119+
let node := (← list.nodes.get)[id]!
120+
if node.value == value then
121+
list.unlink node
122+
list.length.modify (· - 1)
123+
list.free.modify (·.push id)
124+
break
125+
crtId := node.next
126+
127+
end LinkedList
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
{
2+
"authors": [
3+
"oxe-i"
4+
],
5+
"files": {
6+
"solution": [
7+
"LinkedList.lean"
8+
],
9+
"test": [
10+
"LinkedListTest.lean"
11+
],
12+
"example": [
13+
".meta/Example.lean"
14+
]
15+
},
16+
"blurb": "Implement a doubly linked list.",
17+
"source": "Classic computer science topic"
18+
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
# This is an auto-generated file.
2+
#
3+
# Regenerating this file via `configlet sync` will:
4+
# - Recreate every `description` key/value pair
5+
# - Recreate every `reimplements` key/value pair, where they exist in problem-specifications
6+
# - Remove any `include = true` key/value pair (an omitted `include` key implies inclusion)
7+
# - Preserve any other key/value pair
8+
#
9+
# As user-added comments (using the # character) will be removed when this file
10+
# is regenerated, comments can be added via a `comment` key.
11+
12+
[7f7e3987-b954-41b8-8084-99beca08752c]
13+
description = "pop gets element from the list"
14+
15+
[c3f67e5d-cfa2-4c3e-a18f-7ce999c3c885]
16+
description = "push/pop respectively add/remove at the end of the list"
17+
18+
[00ea24ce-4f5c-4432-abb4-cc6e85462657]
19+
description = "shift gets an element from the list"
20+
21+
[37962ee0-3324-4a29-b588-5a4c861e6564]
22+
description = "shift gets first element from the list"
23+
24+
[30a3586b-e9dc-43fb-9a73-2770cec2c718]
25+
description = "unshift adds element at start of the list"
26+
27+
[042f71e4-a8a7-4cf0-8953-7e4f3a21c42d]
28+
description = "pop, push, shift, and unshift can be used in any order"
29+
30+
[88f65c0c-4532-4093-8295-2384fb2f37df]
31+
description = "count an empty list"
32+
33+
[fc055689-5cbe-4cd9-b994-02e2abbb40a5]
34+
description = "count a list with items"
35+
36+
[8272cef5-130d-40ea-b7f6-5ffd0790d650]
37+
description = "count is correct after mutation"
38+
39+
[229b8f7a-bd8a-4798-b64f-0dc0bb356d95]
40+
description = "popping to empty doesn't break the list"
41+
42+
[4e1948b4-514e-424b-a3cf-a1ebbfa2d1ad]
43+
description = "shifting to empty doesn't break the list"
44+
45+
[e8f7c600-d597-4f79-949d-8ad8bae895a6]
46+
description = "deletes the only element"
47+
48+
[fd65e422-51f3-45c0-9fd0-c33da638f89b]
49+
description = "deletes the element with the specified value from the list"
50+
51+
[59db191a-b17f-4ab7-9c5c-60711ec1d013]
52+
description = "deletes the element with the specified value from the list, re-assigns tail"
53+
54+
[58242222-5d39-415b-951d-8128247f8993]
55+
description = "deletes the element with the specified value from the list, re-assigns head"
56+
57+
[ee3729ee-3405-4bd2-9bad-de0d4aa5d647]
58+
description = "deletes the first of two elements"
59+
60+
[47e3b3b4-b82c-4c23-8c1a-ceb9b17cb9fb]
61+
description = "deletes the second of two elements"
62+
63+
[7b420958-f285-4922-b8f9-10d9dcab5179]
64+
description = "delete does not modify the list if the element is not found"
65+
66+
[7e04828f-6082-44e3-a059-201c63252a76]
67+
description = "deletes only the first occurrence"
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
namespace LinkedList
2+
3+
variable {α σ : Type} [BEq α]
4+
5+
structure LinkedList (σ α : Type) where
6+
-- You should define a data structure LinkedList to represent your doubly-linked list here.
7+
8+
def LinkedList.empty : ST σ (LinkedList σ α) :=
9+
sorry
10+
11+
def LinkedList.count (list : LinkedList σ α) : ST σ Nat :=
12+
sorry
13+
14+
def LinkedList.push (value : α) (list : LinkedList σ α) : ST σ Unit :=
15+
sorry
16+
17+
def LinkedList.unshift (value : α) (list : LinkedList σ α) : ST σ Unit :=
18+
sorry
19+
20+
def LinkedList.pop (list : LinkedList σ α) : ST σ (Option α) :=
21+
sorry
22+
23+
def LinkedList.shift (list : LinkedList σ α) : ST σ (Option α) :=
24+
sorry
25+
26+
def LinkedList.delete (value : α) (list : LinkedList σ α) : ST σ Unit :=
27+
sorry
28+
29+
end LinkedList

0 commit comments

Comments
 (0)