From 863f467e2d55294c55af90995612b0d7cbb4a83b Mon Sep 17 00:00:00 2001 From: mgstabrani Date: Thu, 27 Aug 2026 21:12:04 +0700 Subject: [PATCH] data_structure: add linked list implementation --- data_structures/linked_list.jule | 103 ++++++++++++++++++++++++++ data_structures/linked_list_test.jule | 78 +++++++++++++++++++ 2 files changed, 181 insertions(+) create mode 100644 data_structures/linked_list.jule create mode 100644 data_structures/linked_list_test.jule diff --git a/data_structures/linked_list.jule b/data_structures/linked_list.jule new file mode 100644 index 0000000..3a9caae --- /dev/null +++ b/data_structures/linked_list.jule @@ -0,0 +1,103 @@ +// Implementation of a generic singly linked list. +// Reference: https://en.wikipedia.org/wiki/Linked_list + +// node is a single element of a LinkedList. +struct node[T] { + value: T + next: &node[T] +} + +// LinkedList is a generic singly linked list. +struct LinkedList[T] { + head: &node[T] + tail: &node[T] + size: int +} + +impl LinkedList { + // PushFront adds an item to the front of the list. + fn PushFront(mut *self, mut item: T) { + mut n := &node[T]{value: item, next: self.head} + self.head = n + if self.tail == nil { + self.tail = n + } + self.size++ + } + + // PushBack adds an item to the back of the list. + fn PushBack(mut *self, mut item: T) { + mut n := &node[T]{value: item, next: nil} + if self.tail == nil { + self.head = n + self.tail = n + } else { + self.tail.next = n + self.tail = n + } + self.size++ + } + + // PopFront removes and returns the item at the front of the list. + // The second return value reports whether an item was removed; + // it is false when the list is empty. + fn PopFront(mut *self): (item: T, ok: bool) { + if self.head == nil { + return + } + item = self.head.value + mut next := self.head.next + self.head = next + if self.head == nil { + self.tail = nil + } + self.size-- + ok = true + return + } + + // Front returns the item at the front of the list without removing it. + // The second return value reports whether the list has an item; + // it is false when the list is empty. + fn Front(mut *self): (item: T, ok: bool) { + if self.head == nil { + return + } + item = self.head.value + ok = true + return + } + + // Back returns the item at the back of the list without removing it. + // The second return value reports whether the list has an item; + // it is false when the list is empty. + fn Back(mut *self): (item: T, ok: bool) { + if self.tail == nil { + return + } + item = self.tail.value + ok = true + return + } + + // Len returns the number of items in the list. + fn Len(*self): int { + return self.size + } + + // Empty reports whether the list has no items. + fn Empty(*self): bool { + return self.size == 0 + } + + // Slice returns the items of the list as a slice, ordered from front to back. + fn Slice(mut *self): []T { + mut out := make([]T, 0, self.size) + mut n := self.head + for n != nil { + out = append(out, n.value) + n = n.next + } + return out + } +} diff --git a/data_structures/linked_list_test.jule b/data_structures/linked_list_test.jule new file mode 100644 index 0000000..f9b06e5 --- /dev/null +++ b/data_structures/linked_list_test.jule @@ -0,0 +1,78 @@ +#build test + +use "std/slices" +use "std/testing" + +#test +fn testLinkedListPushBack(t: &testing::T) { + mut l := LinkedList[int]{} + t.Assert(l.Empty(), "new list should be empty") + t.Assert(l.Len() == 0, "new list should have length 0") + + l.PushBack(1) + l.PushBack(2) + l.PushBack(3) + t.Assert(!l.Empty(), "list with items should not be empty") + t.Assert(l.Len() == 3, "list should have length 3") + t.Assert(slices::Equal(l.Slice(), [1, 2, 3]), "push back should append in order") +} + +#test +fn testLinkedListPushFront(t: &testing::T) { + mut l := LinkedList[int]{} + l.PushFront(1) + l.PushFront(2) + l.PushFront(3) + t.Assert(slices::Equal(l.Slice(), [3, 2, 1]), "push front should prepend items") +} + +#test +fn testLinkedListMixedPush(t: &testing::T) { + mut l := LinkedList[int]{} + l.PushBack(2) + l.PushFront(1) + l.PushBack(3) + t.Assert(slices::Equal(l.Slice(), [1, 2, 3]), "mixed pushes should keep order") +} + +#test +fn testLinkedListPopFront(t: &testing::T) { + mut l := LinkedList[int]{} + l.PushBack(1) + l.PushBack(2) + + v1, ok1 := l.PopFront() + t.Assert(ok1 && v1 == 1, "pop front should return 1") + v2, ok2 := l.PopFront() + t.Assert(ok2 && v2 == 2, "pop front should return 2") + t.Assert(l.Empty(), "list should be empty after popping all items") + + _, ok3 := l.PopFront() + t.Assert(!ok3, "pop front on empty list should report ok == false") +} + +#test +fn testLinkedListFrontBack(t: &testing::T) { + mut l := LinkedList[int]{} + _, okf := l.Front() + t.Assert(!okf, "front on empty list should report ok == false") + _, okb := l.Back() + t.Assert(!okb, "back on empty list should report ok == false") + + l.PushBack(10) + l.PushBack(20) + f, okf2 := l.Front() + t.Assert(okf2 && f == 10, "front should return 10") + b, okb2 := l.Back() + t.Assert(okb2 && b == 20, "back should return 20") + t.Assert(l.Len() == 2, "front/back should not remove items") +} + +#test +fn testLinkedListGenericString(t: &testing::T) { + mut l := LinkedList[string]{} + l.PushBack("a") + l.PushBack("b") + v, ok := l.PopFront() + t.Assert(ok && v == "a", "generic string list should pop \"a\"") +}