Add examples for Zig language (#242)
* add zig examples * improved zig binary search This commit improves the binary search code in zig. The function has been made generic and the logic has been cleaned up a bit. The code has been updated to work with zig versions >= 0.9 * simplify zig selection sort This commit simplifies the logic of the zig selection sort. It now swaps in place the elements of the array instead of creating another array. This avoids allocating heap memory. The code has also been upgraded to zig version 0.9.1 * make zig recursion examples generic This commit modifies the zig examples for the recursion chapter to be generic. It also updates the code to zig version 0.9.1 * update chapter 4 examples This commit updates the zig examples in chapter 4. In particular examples have been made generic where possible. The code has been updated to zig version 0.9.1 * update zig hash table examples This commit updates the examples for the chapter 5 about hash tables. Some improvements have been done (using a set instead of a map). The code has been updated to zig version 0.9.1 * update breadth first search zig example This commit updates the zig example for the breadth first search algorithm. It adds a unit test and updates the code to zig version 0.9.1 * revamp zig dijkstra example * add comments in dijkstra zig * fix zig greedy algorithm * add test for zig dijkstra * add test for zig greedy algorithm * improve zig chapter 9 exercise This commit improves the zig exercise to comput the longest common subsequence. A main function has been added and the allocator code has been extracted from the `subsequence` function.
This commit is contained in:
161
07_dijkstras_algorithm/zig/dijkstras_algorithm.zig
Normal file
161
07_dijkstras_algorithm/zig/dijkstras_algorithm.zig
Normal file
@@ -0,0 +1,161 @@
|
||||
const std = @import("std");
|
||||
const mem = std.mem;
|
||||
const heap = std.heap;
|
||||
|
||||
pub fn main() !void {
|
||||
var gpa = heap.GeneralPurposeAllocator(.{}){};
|
||||
var arena = heap.ArenaAllocator.init(gpa.allocator());
|
||||
defer arena.deinit();
|
||||
|
||||
var graph = std.StringHashMap(*std.StringHashMap(f32)).init(arena.allocator());
|
||||
|
||||
var start = std.StringHashMap(f32).init(arena.allocator());
|
||||
try start.put("a", 6);
|
||||
try start.put("b", 2);
|
||||
try graph.put("start", &start);
|
||||
|
||||
var a = std.StringHashMap(f32).init(arena.allocator());
|
||||
try a.put("finish", 1);
|
||||
try graph.put("a", &a);
|
||||
|
||||
var b = std.StringHashMap(f32).init(arena.allocator());
|
||||
try b.put("a", 3);
|
||||
try b.put("finish", 5);
|
||||
try graph.put("b", &b);
|
||||
|
||||
var fin = std.StringHashMap(f32).init(arena.allocator());
|
||||
try graph.put("finish", &fin);
|
||||
|
||||
var result = try dijkstra(arena.allocator(), &graph, "start", "finish");
|
||||
|
||||
std.debug.print("Cost from the start to each node:\n", .{});
|
||||
var costs_it = result.costs.iterator();
|
||||
while (costs_it.next()) |cost| {
|
||||
std.debug.print("{s}: {d} ", .{ cost.key_ptr.*, cost.value_ptr.* });
|
||||
}
|
||||
std.debug.print("\n", .{});
|
||||
std.debug.print("\n", .{});
|
||||
std.debug.print("Path from start to finish:\n", .{});
|
||||
var path_it = result.path.iterator();
|
||||
while (path_it.next()) |parent| {
|
||||
std.debug.print("{s} = {?s}\n", .{ parent.key_ptr.*, parent.value_ptr.* });
|
||||
}
|
||||
}
|
||||
|
||||
/// this struct is needed because coercing an anonymous struct literal to an
|
||||
/// error union is not supported by zig yet. Once this is fixed (with the
|
||||
/// self-hosted compiler, see https://github.com/ziglang/zig/issues/11443), the
|
||||
/// dijkstra function could just return:
|
||||
/// ```zig
|
||||
/// return {
|
||||
/// .costs = costs,
|
||||
/// .path = parents,
|
||||
/// };
|
||||
/// ```
|
||||
const dijkstraResult = struct {
|
||||
costs: std.StringHashMap(f32),
|
||||
path: std.StringHashMap(?[]const u8),
|
||||
};
|
||||
|
||||
/// applies the dijkstra algorithm on the provided graph using
|
||||
/// the provided start anf finish nodes.
|
||||
fn dijkstra(
|
||||
allocator: mem.Allocator,
|
||||
graph: *std.StringHashMap(*std.StringHashMap(f32)),
|
||||
start: []const u8,
|
||||
finish: []const u8,
|
||||
) !dijkstraResult {
|
||||
var costs = std.StringHashMap(f32).init(allocator);
|
||||
var parents = std.StringHashMap(?[]const u8).init(allocator);
|
||||
try costs.put(finish, std.math.inf_f32);
|
||||
try parents.put(finish, null);
|
||||
|
||||
// initialize costs and parents maps for the nodes having start as parent
|
||||
var start_graph = graph.get(start);
|
||||
if (start_graph) |sg| {
|
||||
var it = sg.iterator();
|
||||
while (it.next()) |elem| {
|
||||
try costs.put(elem.key_ptr.*, elem.value_ptr.*);
|
||||
try parents.put(elem.key_ptr.*, start);
|
||||
}
|
||||
}
|
||||
|
||||
var processed = std.BufSet.init(allocator);
|
||||
|
||||
var n = findCheapestNode(&costs, &processed);
|
||||
while (n) |node| : (n = findCheapestNode(&costs, &processed)) {
|
||||
var cost = costs.get(node).?;
|
||||
var neighbors = graph.get(node);
|
||||
if (neighbors) |nbors| {
|
||||
var it = nbors.iterator();
|
||||
while (it.next()) |neighbor| {
|
||||
var new_cost = cost + neighbor.value_ptr.*;
|
||||
if (costs.get(neighbor.key_ptr.*).? > new_cost) {
|
||||
// update maps if we found a cheaper path
|
||||
try costs.put(neighbor.key_ptr.*, new_cost);
|
||||
try parents.put(neighbor.key_ptr.*, node);
|
||||
}
|
||||
}
|
||||
}
|
||||
try processed.insert(node);
|
||||
}
|
||||
|
||||
return dijkstraResult{
|
||||
.costs = costs,
|
||||
.path = parents,
|
||||
};
|
||||
}
|
||||
|
||||
/// finds the cheapest node among the not yet processed ones.
|
||||
fn findCheapestNode(costs: *std.StringHashMap(f32), processed: *std.BufSet) ?[]const u8 {
|
||||
var lowest_cost = std.math.inf_f32;
|
||||
var lowest_cost_node: ?[]const u8 = null;
|
||||
|
||||
var it = costs.iterator();
|
||||
while (it.next()) |node| {
|
||||
if (node.value_ptr.* < lowest_cost and !processed.contains(node.key_ptr.*)) {
|
||||
lowest_cost = node.value_ptr.*;
|
||||
lowest_cost_node = node.key_ptr.*;
|
||||
}
|
||||
}
|
||||
|
||||
return lowest_cost_node;
|
||||
}
|
||||
|
||||
test "dijkstra" {
|
||||
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 graph = std.StringHashMap(*std.StringHashMap(f32)).init(arena.allocator());
|
||||
|
||||
var start = std.StringHashMap(f32).init(arena.allocator());
|
||||
try start.put("a", 6);
|
||||
try start.put("b", 2);
|
||||
try graph.put("start", &start);
|
||||
|
||||
var a = std.StringHashMap(f32).init(arena.allocator());
|
||||
try a.put("finish", 1);
|
||||
try graph.put("a", &a);
|
||||
|
||||
var b = std.StringHashMap(f32).init(arena.allocator());
|
||||
try b.put("a", 3);
|
||||
try b.put("finish", 5);
|
||||
try graph.put("b", &b);
|
||||
|
||||
var fin = std.StringHashMap(f32).init(arena.allocator());
|
||||
try graph.put("finish", &fin);
|
||||
|
||||
var result = try dijkstra(arena.allocator(), &graph, "start", "finish");
|
||||
|
||||
try std.testing.expectEqual(result.costs.get("a").?, 5);
|
||||
try std.testing.expectEqual(result.costs.get("b").?, 2);
|
||||
try std.testing.expectEqual(result.costs.get("finish").?, 6);
|
||||
try std.testing.expectEqual(result.path.get("b").?, "start");
|
||||
try std.testing.expectEqual(result.path.get("a").?, "b");
|
||||
try std.testing.expectEqual(result.path.get("finish").?, "a");
|
||||
}
|
||||
Reference in New Issue
Block a user