Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
103 changes: 103 additions & 0 deletions data_structures/linked_list.jule
Original file line number Diff line number Diff line change
@@ -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
}
}
78 changes: 78 additions & 0 deletions data_structures/linked_list_test.jule
Original file line number Diff line number Diff line change
@@ -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\"")
}
Loading