Skip to content

Commit

Permalink
Update examples for Zig (#287)
Browse files Browse the repository at this point in the history
* update zig in chapters 1-6

* fix zig dijkstras algo

* fix zig greedy algo

* fix longest_common_subsequence in zig

* cleanup

* test: use testing allocator
  • Loading branch information
kind84 authored Dec 7, 2024
1 parent 177581a commit 8a13efd
Show file tree
Hide file tree
Showing 13 changed files with 354 additions and 257 deletions.
9 changes: 5 additions & 4 deletions 01_introduction_to_algorithms/zig/binary-search.zig
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,13 @@ pub fn main() void {

fn binarySearch(comptime T: type, list: []const T, item: T) ?usize {
var low: i32 = 0;
var high: i32 = @intCast(i32, list.len) - 1;
const u_high: u32 = @truncate(list.len);
var high: i32 = @intCast(u_high - 1);

return while (low <= high) {
var mid = @divTrunc((low + high), 2);
var m = @intCast(usize, mid);
var guess = list[m];
const mid = @divTrunc((low + high), 2);
const m: usize = @intCast(mid);
const guess = list[m];
if (guess == item) break m;
if (guess > item) {
high = mid - 1;
Expand Down
6 changes: 3 additions & 3 deletions 02_selection_sort/zig/selection_sort.zig
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,12 @@ pub fn main() !void {
}

fn selectionSort(comptime T: type, list: []T) void {
for (list) |_, i| {
for (0..list.len) |i| {
var j = i + 1;
while (j < list.len) : (j += 1) {
if (list[i] > list[j]) {
// swap
var tmp = list[i];
const tmp = list[i];
list[i] = list[j];
list[j] = tmp;
}
Expand All @@ -30,6 +30,6 @@ test "selectionSort" {
selectionSort(i32, s[0..]);

try expect(s.len == exp.len);
for (s) |e, i|
for (s, 0..) |e, i|
try expect(e == exp[i]);
}
2 changes: 1 addition & 1 deletion 03_recursion/zig/04_count.zig
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ test "count" {
var arr0 = [_]i32{};
var arr1 = [_]i32{42};
var arr2 = [_]i32{ 1, 2, 3, 4, 5, 6, 7, 8, 9 };
var tests = [_]struct {
const tests = [_]struct {
arr: []i32,
exp: i32,
}{
Expand Down
4 changes: 2 additions & 2 deletions 04_quicksort/zig/01_loop_sum.zig
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ fn sum(comptime T: type, arr: []T) T {
test "sum" {
var arr0 = [_]i32{ 1, 2, 3, 4 };
var arr1 = [_]i32{};
var tests = [_]struct {
const tests = [_]struct {
arr: []i32,
exp: i32,
}{
Expand All @@ -32,7 +32,7 @@ test "sum" {
};

for (tests) |t| {
var n = sum(@TypeOf(t.exp), t.arr);
const n = sum(@TypeOf(t.exp), t.arr);
try expect(n == t.exp);
}
}
4 changes: 2 additions & 2 deletions 04_quicksort/zig/02_recursive_sum.zig
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ fn sum(comptime T: type, list: []T) T {
test "sum" {
var arr0 = [_]i32{ 1, 2, 3, 4 };
var arr1 = [_]i32{};
var tests = [_]struct {
const tests = [_]struct {
arr: []i32,
exp: i32,
}{
Expand All @@ -31,7 +31,7 @@ test "sum" {
};

for (tests) |t| {
var n = sum(@TypeOf(t.exp), t.arr);
const n = sum(@TypeOf(t.exp), t.arr);
try expect(n == t.exp);
}
}
2 changes: 1 addition & 1 deletion 04_quicksort/zig/03_recursive_count.zig
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ test "count" {
var arr0 = [_]i32{};
var arr1 = [_]i32{42};
var arr2 = [_]i32{ 1, 2, 3, 4, 5, 6, 7, 8, 9 };
var tests = [_]struct {
const tests = [_]struct {
arr: []i32,
exp: i32,
}{
Expand Down
17 changes: 6 additions & 11 deletions 04_quicksort/zig/05_quicksort.zig
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ fn quicksort(comptime T: type, allocator: mem.Allocator, s: []const T) anyerror!
}
}

var low = try quicksort(T, allocator, lower.items);
var high = try quicksort(T, allocator, higher.items);
const low = try quicksort(T, allocator, lower.items);
const high = try quicksort(T, allocator, higher.items);

var res = std.ArrayList(T).init(allocator);
try res.appendSlice(low);
Expand All @@ -44,13 +44,8 @@ fn quicksort(comptime T: type, allocator: mem.Allocator, s: []const T) anyerror!
}

test "quicksort" {
var gpa = heap.GeneralPurposeAllocator(.{}){};
var arena = heap.ArenaAllocator.init(gpa.allocator());
defer {
arena.deinit();
const leaked = gpa.deinit();
if (leaked) std.testing.expect(false) catch @panic("TEST FAIL"); //fail test; can't try in defer as defer is executed after we return
}
var arena = heap.ArenaAllocator.init(std.testing.allocator);
defer arena.deinit();

const tests = [_]struct {
s: []const u8,
Expand All @@ -71,9 +66,9 @@ test "quicksort" {
};

for (tests) |t| {
var res = try quicksort(u8, arena.allocator(), t.s);
const res = try quicksort(u8, arena.allocator(), t.s);
try expect(res.len == t.exp.len);
for (res) |e, i|
for (res, 0..) |e, i|
try expect(e == t.exp[i]);
}
}
82 changes: 48 additions & 34 deletions 04_quicksort/zig/06_quicksort_parallel.zig
Original file line number Diff line number Diff line change
Expand Up @@ -4,60 +4,73 @@ const expect = std.testing.expect;
const heap = std.heap;
const mem = std.mem;

pub const io_mode = .evented;

pub const Error = error{OutOfMemory};
// pub const io_mode = .evented;

pub fn main() !void {
var gpa = heap.GeneralPurposeAllocator(.{}){};
var arena = heap.ArenaAllocator.init(gpa.allocator());
defer arena.deinit();

var s = [_]u8{ 5, 3, 6, 2, 10 };
var u = [_]u8{ 5, 3, 6, 2, 10 };

print("{d}\n", .{try quicksort(arena.allocator(), &s)});
var s = try std.ArrayList(u8).initCapacity(arena.allocator(), u.len);
try quicksort(u8, arena.allocator(), &u, &s);
print("{d}\n", .{s.items});
}

// NOTE: this async version cannot be generic because allocating a frame for a
// generic function is not trivial.
fn quicksort(allocator: mem.Allocator, s: []const u8) Error![]const u8 {
if (s.len < 2) {
return s;
fn quicksort(comptime T: type, allocator: mem.Allocator, u: []const T, s: *std.ArrayList(T)) !void {
if (u.len < 2) {
try s.appendSlice(u);
return;
}

var lower = std.ArrayList(u8).init(allocator);
var higher = std.ArrayList(u8).init(allocator);
var lower = std.ArrayList(T).init(allocator);
var higher = std.ArrayList(T).init(allocator);

const pivot = s[0];
for (s[1..]) |item| {
const pivot = u[0];
for (u[1..]) |item| {
if (item <= pivot) {
try lower.append(item);
} else {
try higher.append(item);
}
}

const low_frame = try allocator.create(@Frame(quicksort));
low_frame.* = async quicksort(allocator, lower.items);
var high = try quicksort(allocator, higher.items);
var low = try await low_frame;
// NOTE: zig has temporary removed the async/await syntax since v0.11.0
//
// const low_frame = try allocator.create(@Frame(quicksort));
// low_frame.* = async quicksort(allocator, lower.items);
// const high = try quicksort(allocator, higher.items);
// const low = try await low_frame;

var low = try std.ArrayList(T).initCapacity(allocator, lower.items.len);
var high = try std.ArrayList(T).initCapacity(allocator, higher.items.len);

var res = std.ArrayList(u8).init(allocator);
try res.appendSlice(low);
try res.append(pivot);
try res.appendSlice(high);
var low_handle = try std.Thread.spawn(
.{},
quicksort,
.{ T, allocator, lower.items, &low },
);
var high_handle = try std.Thread.spawn(
.{},
quicksort,
.{ T, allocator, higher.items, &high },
);
low_handle.join();
high_handle.join();

return res.items;
const lows = try low.toOwnedSlice();
const highs = try high.toOwnedSlice();
try s.appendSlice(lows);
try s.append(pivot);
try s.appendSlice(highs);

return;
}

test "quicksort" {
var gpa = heap.GeneralPurposeAllocator(.{}){};
var arena = heap.ArenaAllocator.init(gpa.allocator());
defer {
arena.deinit();
const leaked = gpa.deinit();
if (leaked) std.testing.expect(false) catch @panic("TEST FAIL"); //fail test; can't try in defer as defer is executed after we return
}
var arena = heap.ArenaAllocator.init(std.testing.allocator);
defer arena.deinit();

const tests = [_]struct {
s: []const u8,
Expand All @@ -78,9 +91,10 @@ test "quicksort" {
};

for (tests) |t| {
var res = try quicksort(arena.allocator(), t.s);
try expect(res.len == t.exp.len);
for (res) |e, i|
try expect(e == t.exp[i]);
var res = std.ArrayList(u8).init(arena.allocator());
try quicksort(u8, arena.allocator(), t.s, &res);
try expect(res.items.len == t.exp.len); // length not matching
for (res.items, 0..) |e, i|
try expect(e == t.exp[i]); // element not matching
}
}
55 changes: 24 additions & 31 deletions 06_breadth-first_search/zig/breadth_first_search.zig
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,10 @@ fn search(
var arena = heap.ArenaAllocator.init(allocator);
defer arena.deinit();
var searched = std.BufSet.init(arena.allocator());
const Q = std.TailQueue([]const u8);
const Q = std.DoublyLinkedList([]const u8);
var queue = Q{};

var name_edges = graph.get(name);
const name_edges = graph.get(name);
if (name_edges) |edges| {
var nodes = try arena.allocator().alloc(Q.Node, edges.len);
var i: usize = 0;
Expand All @@ -53,28 +53,26 @@ fn search(
}

while (queue.len > 0) {
var first = queue.popFirst();
if (first) |person| {
if (!searched.contains(person.data)) {
if (personIsSeller(person.data)) {
std.debug.print("{s} is a mango seller!\n", .{person.data});
return;
} else {
var ee = graph.get(person.data);
if (ee) |edges| {
var nodes = try arena.allocator().alloc(Q.Node, edges.len);
var i: usize = 0;
while (i < edges.len) : (i += 1) {
nodes[i].data = edges[i];
}
for (nodes) |*node| {
queue.append(node);
}
}
try searched.insert(person.data);
}
const person = queue.popFirst() orelse unreachable; // we always have at least one node if len > 0
if (searched.contains(person.data)) {
continue;
}
if (personIsSeller(person.data)) {
std.debug.print("{s} is a mango seller!\n", .{person.data});
return;
}
const ee = graph.get(person.data);
if (ee) |edges| {
var nodes = try arena.allocator().alloc(Q.Node, edges.len);
var i: usize = 0;
while (i < edges.len) : (i += 1) {
nodes[i].data = edges[i];
}
for (nodes) |*node| {
queue.append(node);
}
}
try searched.insert(person.data);
}
}

Expand All @@ -83,14 +81,9 @@ fn personIsSeller(name: []const u8) bool {
}

test "search" {
var gpa = heap.GeneralPurposeAllocator(.{}){};

var graph = std.StringHashMap([][]const u8).init(gpa.allocator());
defer {
graph.deinit();
const leaked = gpa.deinit();
if (leaked) std.testing.expect(false) catch @panic("TEST FAIL"); //fail test; can't try in defer as defer is executed after we return
}
const allocator = std.testing.allocator;
var graph = std.StringHashMap([][]const u8).init(allocator);
defer graph.deinit();

var you = [_][]const u8{ "alice", "bob", "claire" };
var bob = [_][]const u8{ "anuj", "peggy" };
Expand All @@ -110,5 +103,5 @@ test "search" {
try graph.put("thom", &thom);
try graph.put("jonny", &jonny);

try search(gpa.allocator(), &graph, "you");
try search(allocator, &graph, "you");
}
4 changes: 2 additions & 2 deletions 09_dijkstras_algorithm/Golang/01_dijkstras_algorithm.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ func main() {
// Go through all the neighbors of this node.
neighbors := graph[node]

for node, _ := range neighbors {
for node := range neighbors {
new_cost := cost + neighbors[node]
// If it's cheaper to get to this neighbor by going through this node...
if costs[node] > new_cost {
Expand All @@ -71,7 +71,7 @@ func find_lowest_cost_node(costs map[string]float64) string {
lowest_cost := math.Inf(1)
lowest_cost_node := ""

for node, _ := range costs {
for node := range costs {
// fmt.Println("Node:", node, "Value:", value)
cost := costs[node]
// If it's the lowest cost so far and hasn't been processed yet...
Expand Down
Loading

0 comments on commit 8a13efd

Please sign in to comment.