It’s been a while since my last post, and first of all a warm greeting to everyone!
In the meantime, I’ve been working on a module with various common functions used in singly linked lists.
I know Zig already has SinglyLinkedList and for many it may not seem like an achievement, but considering that I started from scratch without any information about what these lists are, I am very happy with the result.
I don’t want to waste your time and I’ll get straight to the point.
About algore
Algore is a collection of small modules implemented in the Zig programming language. If you want, you can call it a library
, but this title seems too much to me, compared to what it is now.
Each module has its own repo, for standalone use case, see the links below.
All code has been tested only with zig version 0.13.0 on Linux Fedora 39.
Examples
//:========================================================================
//: Licensed under the [MIT License](LICENSE).
//:
//: Tested with zig version 0.13.0 on Linux Fedora 39.
//:========================================================================
const std = @import("std");
const assert = std.debug.assert;
const countDigit = @import("algore").digit.countIterative;
const SinglyLinkedList = @import("algore").singly_linked_list.SinglyLinkedList;
pub fn main() !void {
//^--------------------------------------------------------------
//^ Module `digit`
//^--------------------------------------------------------------
{
//+ Supported types.
{
assert(countDigit(0) == 1);
assert(countDigit(0.0) == 1);
assert(countDigit(@as(u32, 0)) == 1);
assert(countDigit(@as(i32, 0)) == 1);
assert(countDigit(@as(f32, 0.0)) == 1);
}
//+ Unsupported types, returns `null`.
{
assert(countDigit(std.math.maxInt(u129)) == null);
assert(countDigit(@as(u129, std.math.maxInt(u129))) == null);
assert(countDigit(std.math.minInt(i130)) == null);
assert(countDigit(@as(i130, std.math.minInt(i130))) == null);
assert(countDigit(@as(f16, 2050)) == null);
}
}
//^--------------------------------------------------------------
//^ Module `singly_linked_list`
//^--------------------------------------------------------------
{
const Typ = usize;
const Lst = SinglyLinkedList(Typ);
const Nod = Lst.Node;
var nod0 = Nod{ .data = 0, .next = null };
var nod1 = Nod{ .data = 1, .next = null };
var nod2 = Nod{ .data = 2, .next = null };
var nod3 = Nod{ .data = 3, .next = null };
var nod4 = Nod{ .data = 4, .next = null };
var lst0 = Lst{};
//+ Node `data` field type.
{
assert(Nod.Data == Typ);
assert(@TypeOf(nod0).Data == Typ);
}
//+ Node `link()` and `unlink()` functions
{
assert(nod0.length() == 1);
nod0.link(&nod1);
nod0.link(&nod2);
nod0.link(&nod3);
nod0.link(&nod4);
assert(nod0.length() == 5);
nod0.unlink(&nod4);
nod0.unlink(&nod3);
nod0.unlink(&nod2);
nod0.unlink(&nod1);
assert(nod0.length() == 1);
}
//+ List `data` field type.
{
assert(Lst.Node.Data == Typ);
assert(@TypeOf(lst0).Node.Data == Typ);
}
//+ List `insertLast()` and `removeLast()` functions
{
assert(lst0.length() == 0);
assert(nod0.length() == 1);
lst0.insertLast(&nod0);
lst0.insertLast(&nod1);
lst0.insertLast(&nod2);
lst0.insertLast(&nod3);
lst0.insertLast(&nod4);
assert(lst0.length() == 5);
assert(nod0.length() == 5);
lst0.removeLast();
lst0.removeLast();
lst0.removeLast();
lst0.removeLast();
lst0.removeLast();
assert(lst0.length() == 0);
assert(nod0.length() == 1);
}
}
}
Module singly_linked_list
The module contains functions used in singly linked lists.
These functions do not make assertions, can lead to infinite loops in code and cycles in nodes or lists.
As a general rule, cycles should be broken before any other changes. Almost all functions are candidates for applying this rule. Keep this in mind when infinite loops occur in code and their causes are not very obvious.
Node functions
- link() - link the
node
to another node - unlink() - unlink the
node
from another node - insert() - insert the
node
in node, beforetail
node - remove() - remove the
node
from node, beforetail
node - last() - return the last node of node
- find() - return the
node
, ifnode
is in node, otherwisenull
- previous() - return the previous node of
node
, ifnode
is in node, otherwisenull
- loop() - return the loop node of node, if node has a loop, otherwise
null
- breakLoop() - break the node cycle
- reset() - reset the node and its nodes
- reverse() - reverse the link order of nodes, from a node
- length() - return the node length
- isLinked() - assert the link status of node
- hasNode() - assert the
node
status in node - hasLoop() - assert the loop status of node
- printNode() - print the node data values
- toList() - convert the node to a list
List functions
- insertFirst() - insert the
node
at first position in list - insertLast() - insert the
node
at last position in list - insertBefore() - insert the
node
in list, beforetail
node - removeFirst() - remove the first node from list
- removeLast() - remove the last node from list
- removeBefore() - remove the
node
from list, beforetail
node - removeNode() - remove the
node
from list - removeAll() - remove all nodes from the list
- last() - return the last node of list, if list is not empty, otherwise
null
- find() - return the
node
, ifnode
is in list, otherwisenull
- previous() - return the previous node of
node
, ifnode
is in list, otherwisenull
- loop() - return the loop node of list, if list has a loop, otherwise
null
- breakLoop() - break the list cycle
- reset() - reset the list and its nodes
- reverse() - reverse the list in-place
- length() - return the list length
- isEmpty() - assert the empty status of list
- isLinked() - assert the link status of list
- hasNode() - assert the
node
status in list - hasLoop() - assert the loop status of list
- printList() - print the list data values
- toNode() - convert the list to a node, if the list is not empty, otherwise return
null
Module digit
The module contains functions that return the number of digits for a number
, using various algorithms.
All functions return the number of digits for a supported number
, otherwise null
.
After working quite a bit with the optional types in the singly_linked_list
module, I started to like them.
I can mention a few advantages I have seen in using optionals:
- makes the code more clearer and the development process much, much faster
- no more struggles to make error messages, which aren’t very helpful anyway, most users end up checking the code itself
- makes the testing process safer and closer to real use cases
- the
error cases
returnnull
, which can be tested very easily
- the
- indirectly makes you write a better and more on-topic documentation
- it’s easier to write a few lines of
what it does
than to cover all error cases with messageswhat it doesn't
- it’s easier to write a few lines of
I’m not saying that error sets and messages don’t have their place, but in general, optionals can save you a lot of time and hassle.
Back on track, these functions are actually the old digit counting functions that now return null
and are in a single file and module.
If this change bothers anyone, I’m very sorry.
Supported number
types and ranges:
- .ComptimeInt, .Int: ∓340282366920938463463374607431768211455
- min(i129) = -340282366920938463463374607431768211456 it is accepted to cover the type
- .ComptimeFloat, .Float:
- comptime_float: ∓10384593717069655257060992658440193
- f16: ∓2049
- f32: ∓16777217
- f64: ∓9007199254740993
- f80: ∓36893488147419103234
- f128: ∓10384593717069655257060992658440193
Note that for float numbers the type is important, for example:
- 2050 as f16: returns
null
- 2050 as f32: returns 4, same as for any type greater than f16
Negative numbers are converted to their absolute value.
Digit functions
- countIterative() - uses an iterative algorithm
- countLogarithmic() - uses a logarithmic algorithm
- countLookup() - uses a lookup algorithm
- countRecursive() - uses a recursive algorithm
- countStringify() - uses a stringify algorithm
- countSwitcher() - uses a switcher algorithm
Final note
More detailed examples can be found in the demo
files from each repo. Some parts of the demo code are inactive (commented) to clarify what it does, just choose what you want to run.
Regarding the singly_linked_list
module, any feedback is greatly appreciated, especially if a common function is missing. I’m very interested in cases that cannot be solved with the existing functions.
For example, sorting nodes is not a common function, it depends on the data type which can be a number, a string, … even a structure, so it’s outside of the module scope.
Regarding algore
itself, I noticed that the documentation is disconnected, the breadcrumbs
at the top of webpages. A solution can be an intermediate file for each module, if I remember correctly, but I want to avoid doing that unless it is functionally necessary. If there is no another solution, it will remain like this for now.
Anyway, overall the modules work pretty well and any feedback is appreciated.
As usual, I wish you all the best!