diff --git a/exercises/practice/linked-list/.meta/example.lua b/exercises/practice/linked-list/.meta/example.lua index eb4f2cbf..0d2b21f0 100644 --- a/exercises/practice/linked-list/.meta/example.lua +++ b/exercises/practice/linked-list/.meta/example.lua @@ -15,7 +15,9 @@ end function LinkedList:pop() local v = self._head.v self._head = self._head.next - if not self._head then + if self._head then + self._head.prev = nil + else self._tail = nil end return v @@ -37,6 +39,8 @@ function LinkedList:shift() self._tail = self._tail.prev if self._tail then self._tail.next = nil + else + self._head = nil end return v end @@ -52,21 +56,21 @@ function LinkedList:count() end function LinkedList:delete(v) + if self._head.v == v then + self:pop() + return + end + local current = self._head while current do if current.v == v then - if self._head == current then - self._head = current.next - end - if self._tail == current then - self._tail = current.prev - end if current.prev then current.prev.next = current.next end if current.next then current.next.prev = current.prev end + return end current = current.next end diff --git a/exercises/practice/linked-list/.meta/spec_generator.lua b/exercises/practice/linked-list/.meta/spec_generator.lua new file mode 100644 index 00000000..52f9b9d2 --- /dev/null +++ b/exercises/practice/linked-list/.meta/spec_generator.lua @@ -0,0 +1,19 @@ +return { + module_name = 'LinkedList', + + generate_test = function(case) + local lines = { 'local list = LinkedList()' } + + for _, operation in ipairs(case.input.operations) do + if operation.value then + table.insert(lines, ('list:%s(%s)'):format(operation.operation, operation.value)) + elseif operation.expected then + table.insert(lines, ('assert.equal(%s, list:%s())'):format(operation.expected, operation.operation)) + else + table.insert(lines, ('list:%s()'):format(operation.operation)) + end + end + + return table.concat(lines, '\n') + end +} diff --git a/exercises/practice/linked-list/.meta/tests.toml b/exercises/practice/linked-list/.meta/tests.toml new file mode 100644 index 00000000..96906d2c --- /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_spec.lua b/exercises/practice/linked-list/linked-list_spec.lua index 922259ee..ce49f7c6 100644 --- a/exercises/practice/linked-list/linked-list_spec.lua +++ b/exercises/practice/linked-list/linked-list_spec.lua @@ -1,113 +1,177 @@ local LinkedList = require('linked-list') describe('linked-list', function() - it('should be able to pop pushed elements', function() + it('pop gets element from the list', function() local list = LinkedList() - list:push(10) - list:push(20) - assert.equal(20, list:pop()) - assert.equal(10, list:pop()) + list:push(7) + assert.equal(7, list:pop()) end) - it('should be able to shift pushed elements', function() + it('push/pop respectively add/remove at the end of the list', function() local list = LinkedList() - list:push(10) - list:push(20) - assert.equal(10, list:shift()) - assert.equal(20, list:shift()) + list:push(11) + list:push(13) + assert.equal(13, list:pop()) + assert.equal(11, list:pop()) end) - it('should be able to shift unshifted elements', function() + it('shift gets an element from the list', function() local list = LinkedList() - list:unshift(10) - list:unshift(20) - assert.equal(20, list:shift()) - assert.equal(10, list:shift()) + list:push(17) + assert.equal(17, list:shift()) end) - it('should be able to pop unshifted elements', function() + it('shift gets first element from the list', function() local list = LinkedList() - list:unshift(10) - list:unshift(20) - assert.equal(10, list:pop()) - assert.equal(20, list:pop()) + list:push(23) + list:push(5) + assert.equal(23, list:shift()) + assert.equal(5, list:shift()) end) - it('should be able to count its elements', function() + it('unshift adds element at start of the list', function() + local list = LinkedList() + list:unshift(23) + list:unshift(5) + assert.equal(5, list:shift()) + assert.equal(23, list:shift()) + end) + + it('pop, push, shift, and unshift can be used in any order', function() + local list = LinkedList() + list:push(1) + list:push(2) + assert.equal(2, list:pop()) + list:push(3) + assert.equal(1, list:shift()) + list:unshift(4) + list:push(5) + assert.equal(4, list:shift()) + assert.equal(5, list:pop()) + assert.equal(3, list:shift()) + end) + + it('count an empty list', function() local list = LinkedList() assert.equal(0, list:count()) - list:push(10) - assert.equal(1, list:count()) - list:push(20) - assert.equal(2, list:count()) + end) + it('count a list with items', function() + local list = LinkedList() + list:push(37) + list:push(1) + assert.equal(2, list:count()) end) - it('should count correctly after a shift', function() + it('count is correct after mutation', function() local list = LinkedList() - list:push(10) - list:push(20) + list:push(31) + assert.equal(1, list:count()) + list:unshift(43) + assert.equal(2, list:count()) list:shift() assert.equal(1, list:count()) + list:pop() + assert.equal(0, list:count()) end) - it('should count correctly after a pop', function() + it('popping to empty doesn\'t break the list', function() local list = LinkedList() - list:push(10) - list:push(20) + list:push(41) + list:push(59) list:pop() + list:pop() + list:push(47) assert.equal(1, list:count()) + assert.equal(47, list:pop()) end) - it('should be able to delete from the beginning of the list', function() + it('shifting to empty doesn\'t break the list', function() local list = LinkedList() - list:push(10) - list:push(20) - list:push(30) - list:delete(30) - assert.equal(2, list:count()) - assert.equal(20, list:pop()) - assert.equal(10, list:shift()) + list:push(41) + list:push(59) + list:shift() + list:shift() + list:push(47) + assert.equal(1, list:count()) + assert.equal(47, list:shift()) + end) + + it('deletes the only element', function() + local list = LinkedList() + list:push(61) + list:delete(61) + assert.equal(0, list:count()) end) - it('should be able to delete from the middle of the list', function() + it('deletes the element with the specified value from the list', function() local list = LinkedList() - list:push(10) - list:push(20) - list:push(30) - list:delete(20) + list:push(71) + list:push(83) + list:push(79) + list:delete(83) assert.equal(2, list:count()) - assert.equal(30, list:pop()) - assert.equal(10, list:shift()) + assert.equal(79, list:pop()) + assert.equal(71, list:shift()) end) - it('should be able to delete from the end of the list', function() + it('deletes the element with the specified value from the list, re-assigns tail', function() local list = LinkedList() - list:push(10) - list:push(20) - list:push(30) - list:delete(10) + list:push(71) + list:push(83) + list:push(79) + list:delete(83) assert.equal(2, list:count()) - assert.equal(30, list:pop()) - assert.equal(20, list:shift()) + assert.equal(79, list:pop()) + assert.equal(71, list:pop()) end) - it('should delete all elements with the matching value', function() + it('deletes the element with the specified value from the list, re-assigns head', function() local list = LinkedList() - list:push(10) - list:push(20) - list:push(20) - list:push(30) - list:delete(20) + list:push(71) + list:push(83) + list:push(79) + list:delete(83) assert.equal(2, list:count()) - assert.equal(30, list:pop()) - assert.equal(10, list:shift()) + assert.equal(71, list:shift()) + assert.equal(79, list:shift()) end) - it('should be able to delete the only element', function() + it('deletes the first of two elements', function() local list = LinkedList() - list:push(10) - list:delete(10) - assert.equal(0, list:count()) + list:push(97) + list:push(101) + list:delete(97) + assert.equal(1, list:count()) + assert.equal(101, list:pop()) + end) + + it('deletes the second of two elements', function() + local list = LinkedList() + list:push(97) + list:push(101) + list:delete(101) + assert.equal(1, list:count()) + assert.equal(97, list:pop()) + end) + + it('delete does not modify the list if the element is not found', function() + local list = LinkedList() + list:push(89) + list:delete(103) + assert.equal(1, list:count()) + end) + + it('deletes only the first occurrence', function() + local list = LinkedList() + list:push(73) + list:push(9) + list:push(9) + list:push(107) + list:delete(9) + assert.equal(3, list:count()) + assert.equal(107, list:pop()) + assert.equal(9, list:pop()) + assert.equal(73, list:pop()) end) end)