From 88909faafccc102ec8a3127e4526ef8118a267d4 Mon Sep 17 00:00:00 2001 From: Glenn Jackman Date: Wed, 24 Dec 2025 18:54:48 -0500 Subject: [PATCH] linked-list --- config.json | 8 + exercises/practice/linked-list/.busted | 5 + .../linked-list/.docs/instructions.md | 26 +++ .../linked-list/.docs/introduction.md | 6 + .../practice/linked-list/.meta/config.json | 18 ++ .../practice/linked-list/.meta/example.moon | 68 ++++++++ .../linked-list/.meta/spec_generator.moon | 17 ++ .../practice/linked-list/.meta/tests.toml | 67 ++++++++ .../practice/linked-list/linked_list.moon | 21 +++ .../linked-list/linked_list_spec.moon | 157 ++++++++++++++++++ 10 files changed, 393 insertions(+) create mode 100644 exercises/practice/linked-list/.busted create mode 100644 exercises/practice/linked-list/.docs/instructions.md create mode 100644 exercises/practice/linked-list/.docs/introduction.md create mode 100644 exercises/practice/linked-list/.meta/config.json create mode 100644 exercises/practice/linked-list/.meta/example.moon create mode 100644 exercises/practice/linked-list/.meta/spec_generator.moon create mode 100644 exercises/practice/linked-list/.meta/tests.toml create mode 100644 exercises/practice/linked-list/linked_list.moon create mode 100644 exercises/practice/linked-list/linked_list_spec.moon diff --git a/config.json b/config.json index 2f3e073..e8f1b20 100644 --- a/config.json +++ b/config.json @@ -350,6 +350,14 @@ "practices": [], "prerequisites": [], "difficulty": 4 + }, + { + "slug": "linked-list", + "name": "Linked List", + "uuid": "e4d7413a-6429-4d6e-bcfd-580c9a449d4e", + "practices": [], + "prerequisites": [], + "difficulty": 4 } ] }, diff --git a/exercises/practice/linked-list/.busted b/exercises/practice/linked-list/.busted new file mode 100644 index 0000000..86b84e7 --- /dev/null +++ b/exercises/practice/linked-list/.busted @@ -0,0 +1,5 @@ +return { + default = { + ROOT = { '.' } + } +} diff --git a/exercises/practice/linked-list/.docs/instructions.md b/exercises/practice/linked-list/.docs/instructions.md new file mode 100644 index 0000000..edf4055 --- /dev/null +++ b/exercises/practice/linked-list/.docs/instructions.md @@ -0,0 +1,26 @@ +# Instructions + +Your team has decided to use a doubly linked list to represent each train route in the schedule. +Each station along the train's route will be represented by a node in the linked list. + +You don't need to worry about arrival and departure times at the stations. +Each station will simply be represented by a number. + +Routes can be extended, adding stations to the beginning or end of a route. +They can also be shortened by removing stations from the beginning or the end of a route. + +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. + +The size of a route is measured not by how far the train travels, but by how many stations it stops at. + +~~~~exercism/note +The linked list is a fundamental data structure in computer science, often used in the implementation of other data structures. +As the name suggests, it is a list of nodes that are linked together. +It is a list of "nodes", where each node links to its neighbor or neighbors. +In a **singly linked list** each node links only to the node that follows it. +In a **doubly linked list** each node links to both the node that comes before, as well as the node that comes after. + +If you want to dig deeper into linked lists, check out [this article][intro-linked-list] that explains it using nice drawings. + +[intro-linked-list]: https://medium.com/basecs/whats-a-linked-list-anyway-part-1-d8b7e6508b9d +~~~~ diff --git a/exercises/practice/linked-list/.docs/introduction.md b/exercises/practice/linked-list/.docs/introduction.md new file mode 100644 index 0000000..6e83ae7 --- /dev/null +++ b/exercises/practice/linked-list/.docs/introduction.md @@ -0,0 +1,6 @@ +# Introduction + +You are working on a project to develop a train scheduling system for a busy railway network. + +You've been asked to develop a prototype for the train routes in the scheduling system. +Each route consists of a sequence of train stations that a given train stops at. diff --git a/exercises/practice/linked-list/.meta/config.json b/exercises/practice/linked-list/.meta/config.json new file mode 100644 index 0000000..dfd22fb --- /dev/null +++ b/exercises/practice/linked-list/.meta/config.json @@ -0,0 +1,18 @@ +{ + "authors": [ + "glennj" + ], + "files": { + "solution": [ + "linked_list.moon" + ], + "test": [ + "linked_list_spec.moon" + ], + "example": [ + ".meta/example.moon" + ] + }, + "blurb": "Implement a doubly linked list.", + "source": "Classic computer science topic" +} diff --git a/exercises/practice/linked-list/.meta/example.moon b/exercises/practice/linked-list/.meta/example.moon new file mode 100644 index 0000000..213dec2 --- /dev/null +++ b/exercises/practice/linked-list/.meta/example.moon @@ -0,0 +1,68 @@ +class LinkedList + new: (@head = nil, @tail = nil) => + + push: (value) => + node = {:value, next: nil, prev: nil} + if @tail + @tail.next = node + node.prev = @tail + else + @head = node + @tail = node + + unshift: (value) => + node = {:value, next: nil, prev: nil} + if @head + @head.prev = node + node.next = @head + else + @tail = node + @head = node + + pop: => + return unless @tail + value = @tail.value + if @head == @tail + @head, @tail = nil, nil + else + @tail.prev.next = nil + @tail = @tail.prev + value + + shift: => + return unless @head + value = @head.value + if @head == @tail + @head, @tail = nil, nil + else + @head.next.prev = nil + @head = @head.next + value + + count: => + n = 0 + node = @head + while node != nil + n += 1 + node = node.next + n + + delete: (value) => + node = @head + while node != nil + if node.value == value + if node.prev and node.next + node.prev.next = node.next + node.next.prev = node.prev + node = nil + elseif node.next + -- this is the head + @shift! + elseif node.prev + -- this is the tail + @pop! + else + @head, @tail = nil, nil + break + -- end if + node = node.next diff --git a/exercises/practice/linked-list/.meta/spec_generator.moon b/exercises/practice/linked-list/.meta/spec_generator.moon new file mode 100644 index 0000000..f82bb6e --- /dev/null +++ b/exercises/practice/linked-list/.meta/spec_generator.moon @@ -0,0 +1,17 @@ +{ + module_name: 'LinkedList', + + generate_test: (case, level) -> + lines = { + "list = LinkedList!", + } + for op in *case.input.operations + if op.value + table.insert lines, "list\\#{op.operation} #{op.value}" + elseif op.expected + table.insert lines, "assert.are.equal #{op.expected}, list\\#{op.operation}!" + else + table.insert lines, "list\\#{op.operation}!" + + table.concat [indent line, level for line in *lines], '\n' +} diff --git a/exercises/practice/linked-list/.meta/tests.toml b/exercises/practice/linked-list/.meta/tests.toml new file mode 100644 index 0000000..96906d2 --- /dev/null +++ b/exercises/practice/linked-list/.meta/tests.toml @@ -0,0 +1,67 @@ +# This is an auto-generated file. +# +# Regenerating this file via `configlet sync` will: +# - Recreate every `description` key/value pair +# - Recreate every `reimplements` key/value pair, where they exist in problem-specifications +# - Remove any `include = true` key/value pair (an omitted `include` key implies inclusion) +# - Preserve any other key/value pair +# +# As user-added comments (using the # character) will be removed when this file +# is regenerated, comments can be added via a `comment` key. + +[7f7e3987-b954-41b8-8084-99beca08752c] +description = "pop gets element from the list" + +[c3f67e5d-cfa2-4c3e-a18f-7ce999c3c885] +description = "push/pop respectively add/remove at the end of the list" + +[00ea24ce-4f5c-4432-abb4-cc6e85462657] +description = "shift gets an element from the list" + +[37962ee0-3324-4a29-b588-5a4c861e6564] +description = "shift gets first element from the list" + +[30a3586b-e9dc-43fb-9a73-2770cec2c718] +description = "unshift adds element at start of the list" + +[042f71e4-a8a7-4cf0-8953-7e4f3a21c42d] +description = "pop, push, shift, and unshift can be used in any order" + +[88f65c0c-4532-4093-8295-2384fb2f37df] +description = "count an empty list" + +[fc055689-5cbe-4cd9-b994-02e2abbb40a5] +description = "count a list with items" + +[8272cef5-130d-40ea-b7f6-5ffd0790d650] +description = "count is correct after mutation" + +[229b8f7a-bd8a-4798-b64f-0dc0bb356d95] +description = "popping to empty doesn't break the list" + +[4e1948b4-514e-424b-a3cf-a1ebbfa2d1ad] +description = "shifting to empty doesn't break the list" + +[e8f7c600-d597-4f79-949d-8ad8bae895a6] +description = "deletes the only element" + +[fd65e422-51f3-45c0-9fd0-c33da638f89b] +description = "deletes the element with the specified value from the list" + +[59db191a-b17f-4ab7-9c5c-60711ec1d013] +description = "deletes the element with the specified value from the list, re-assigns tail" + +[58242222-5d39-415b-951d-8128247f8993] +description = "deletes the element with the specified value from the list, re-assigns head" + +[ee3729ee-3405-4bd2-9bad-de0d4aa5d647] +description = "deletes the first of two elements" + +[47e3b3b4-b82c-4c23-8c1a-ceb9b17cb9fb] +description = "deletes the second of two elements" + +[7b420958-f285-4922-b8f9-10d9dcab5179] +description = "delete does not modify the list if the element is not found" + +[7e04828f-6082-44e3-a059-201c63252a76] +description = "deletes only the first occurrence" diff --git a/exercises/practice/linked-list/linked_list.moon b/exercises/practice/linked-list/linked_list.moon new file mode 100644 index 0000000..b8a02d0 --- /dev/null +++ b/exercises/practice/linked-list/linked_list.moon @@ -0,0 +1,21 @@ +class LinkedList + new: (...) => + error 'Implement the constructor' + + push: (value) => + error 'Implement the push method' + + unshift: (value) => + error 'Implement the unshift method' + + pop: => + error 'Implement the pop method' + + shift: => + error 'Implement the shift method' + + count: => + error 'Implement the count method' + + delete: (value) => + error 'Implement the delete method' diff --git a/exercises/practice/linked-list/linked_list_spec.moon b/exercises/practice/linked-list/linked_list_spec.moon new file mode 100644 index 0000000..6333dfb --- /dev/null +++ b/exercises/practice/linked-list/linked_list_spec.moon @@ -0,0 +1,157 @@ +LinkedList = require 'linked_list' + +describe 'linked-list', -> + it 'pop gets element from the list', -> + list = LinkedList! + list\push 7 + assert.are.equal 7, list\pop! + + pending 'push/pop respectively add/remove at the end of the list', -> + list = LinkedList! + list\push 11 + list\push 13 + assert.are.equal 13, list\pop! + assert.are.equal 11, list\pop! + + pending 'shift gets an element from the list', -> + list = LinkedList! + list\push 17 + assert.are.equal 17, list\shift! + + pending 'shift gets first element from the list', -> + list = LinkedList! + list\push 23 + list\push 5 + assert.are.equal 23, list\shift! + assert.are.equal 5, list\shift! + + pending 'unshift adds element at start of the list', -> + list = LinkedList! + list\unshift 23 + list\unshift 5 + assert.are.equal 5, list\shift! + assert.are.equal 23, list\shift! + + pending 'pop, push, shift, and unshift can be used in any order', -> + list = LinkedList! + list\push 1 + list\push 2 + assert.are.equal 2, list\pop! + list\push 3 + assert.are.equal 1, list\shift! + list\unshift 4 + list\push 5 + assert.are.equal 4, list\shift! + assert.are.equal 5, list\pop! + assert.are.equal 3, list\shift! + + pending 'count an empty list', -> + list = LinkedList! + assert.are.equal 0, list\count! + + pending 'count a list with items', -> + list = LinkedList! + list\push 37 + list\push 1 + assert.are.equal 2, list\count! + + pending 'count is correct after mutation', -> + list = LinkedList! + list\push 31 + assert.are.equal 1, list\count! + list\unshift 43 + assert.are.equal 2, list\count! + list\shift! + assert.are.equal 1, list\count! + list\pop! + assert.are.equal 0, list\count! + + pending "popping to empty doesn't break the list", -> + list = LinkedList! + list\push 41 + list\push 59 + list\pop! + list\pop! + list\push 47 + assert.are.equal 1, list\count! + assert.are.equal 47, list\pop! + + pending "shifting to empty doesn't break the list", -> + list = LinkedList! + list\push 41 + list\push 59 + list\shift! + list\shift! + list\push 47 + assert.are.equal 1, list\count! + assert.are.equal 47, list\shift! + + pending 'deletes the only element', -> + list = LinkedList! + list\push 61 + list\delete 61 + assert.are.equal 0, list\count! + + pending 'deletes the element with the specified value from the list', -> + list = LinkedList! + list\push 71 + list\push 83 + list\push 79 + list\delete 83 + assert.are.equal 2, list\count! + assert.are.equal 79, list\pop! + assert.are.equal 71, list\shift! + + pending 'deletes the element with the specified value from the list, re-assigns tail', -> + list = LinkedList! + list\push 71 + list\push 83 + list\push 79 + list\delete 83 + assert.are.equal 2, list\count! + assert.are.equal 79, list\pop! + assert.are.equal 71, list\pop! + + pending 'deletes the element with the specified value from the list, re-assigns head', -> + list = LinkedList! + list\push 71 + list\push 83 + list\push 79 + list\delete 83 + assert.are.equal 2, list\count! + assert.are.equal 71, list\shift! + assert.are.equal 79, list\shift! + + pending 'deletes the first of two elements', -> + list = LinkedList! + list\push 97 + list\push 101 + list\delete 97 + assert.are.equal 1, list\count! + assert.are.equal 101, list\pop! + + pending 'deletes the second of two elements', -> + list = LinkedList! + list\push 97 + list\push 101 + list\delete 101 + assert.are.equal 1, list\count! + assert.are.equal 97, list\pop! + + pending 'delete does not modify the list if the element is not found', -> + list = LinkedList! + list\push 89 + list\delete 103 + assert.are.equal 1, list\count! + + pending 'deletes only the first occurrence', -> + list = LinkedList! + list\push 73 + list\push 9 + list\push 9 + list\push 107 + list\delete 9 + assert.are.equal 3, list\count! + assert.are.equal 107, list\pop! + assert.are.equal 9, list\pop! + assert.are.equal 73, list\pop!