Zig语言编程示例
部分代码非本人编写。
以下部分Zig代码可以在AttemptThisOnline运行。
- 推荐些阅读材料
- Zig语言简介
- 在线执行Zig的平台
- debug-print
- 函数返回类型
- allocator有哪些
- for-loop和while-loop
- for enumerate
- ArrayList
- Arrays
- 字符的ASCII码
- 为什么zig 0.14.0中不能使用std.mem.copy
- zig 0.14.1中std.fs.openFileAbsolute读取文件
/proc/stat
并处理,显示Linux CPU utilization - zig 0.14.0中split函数被废弃了
- 实战——变长二维数组
- 实战——算一下组合数
- 实战——考虑溢出的整数加法
- 实战——Zig 0.14.0中使用随机数
- 实战——使用Zig实现一些图论算法
- There is no std.BTreeSet in Zig's standard library
推荐些阅读材料
Zig by Example
官方文档
Zig中文教程,非官方
Zig语言简介
Zig 是一种命令式、通用、静态类型、编译的系统编程语言,由 Andrew Kelley 设计。 它旨在替代C编程语言,其目标是更小,编程更简单,同时还提供现代功能,新的优化和各种安全机制, 同时不像其他语言那样对运行时安全性的要求。
语言简单性的改进与流控制、函数调用、库导入、变量声明和 Unicode 支持有关。 此外,该语言不使用宏或预处理器指令。从现代语言中采用的功能包括添加编译时泛型类型, 允许函数处理各种数据,以及一小组新的编译器指令,以允许使用反射访问有关这些类型的信息。 Zig 还旨在提高代码的安全性,它不提供垃圾回收(GC),但是使用可选类型代替 null
,这避免了空指针的出现。
在线执行Zig的平台
有很多
以下列出了,可以在线执行 zig 的平台:
zig-playground 版本:info: Zig version: 0.12.0-dev.126+fe424bd4b
zig-play 版本:info: Zig version: 0.13.0
ATO上的zig版本是:info: Zig version: 0.13.0
debug-print
const std = @import("std");
std.debug.print("z={},d={},n={},k={}\n", .{z, d, n, k});
函数返回类型
看起来pub fn main() !void
要比pub fn main() void
好用。
这是联合错误类型,该语法将会告诉 zig 编译器会返回错误或者值,此处的意思是如果有返回值,一定是一个错误。
pub fn main() !void {
const result = try generateSequence(5, 13);
for (result) |item| {
std.debug.print("{}, ", .{item});
}
std.debug.print("\n", .{});
}
allocator有哪些
std.heap.page_allocator
const allocator = std.heap.page_allocator;
const memory = try allocator.alloc(u8, 100);
defer allocator.free(memory);
try expect(memory.len == 100);
try expect(@TypeOf(memory) == []u8);
const byte = try std.heap.page_allocator.create(u8);
defer std.heap.page_allocator.destroy(byte);
byte.* = 128;
std.heap.FixedBufferAllocator
var buffer: [1000]u8 = undefined;
var fba = std.heap.FixedBufferAllocator.init(&buffer);
const allocator = fba.allocator();
const memory = try allocator.alloc(u8, 100);
defer allocator.free(memory);
try expect(memory.len == 100);
try expect(@TypeOf(memory) == []u8);
std.heap.GeneralPurposeAllocator
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
const allocator = gpa.allocator();
defer {
const deinit_status = gpa.deinit();
//fail test; can't try in defer as defer is executed after we return
if (deinit_status == .leak) expect(false) catch @panic("TEST FAIL");
}
const bytes = try allocator.alloc(u8, 100);
defer allocator.free(bytes);
for-loop和while-loop
const std = @import("std");
const print = std.debug.print;
pub fn main() !void {
var array = [_]u32{ 1, 2, 3 };
for (array) |elem| {
print("by val: {}\n", .{elem});
}
for (&array) |*elem| {
elem.* += 100;
print("by ref: {}\n", .{elem.*});
}
for (array, &array) |val, *ref| {
_ = val;
_ = ref;
}
for(0..4) |my_index| {
print("index: {}\n", .{my_index});
}
for (0.., array) |i, elem| {
print("{}: {}\n", .{ i, elem });
}
for (array) |_| {}
var a: usize = 0;
var b: usize = 0;
var c: usize = 0;
var d: usize = 0;
while (a < 2) {
print("a: {}\n", .{a});
a += 1;
}
while (b < 2) : (b += 1) {
print("b: {}\n", .{b});
}
while (c < 4) : ({
c += 1;
c += 1;
}) {
print("c: {}\n", .{c});
}
while (true) {
break;
}
while (true) : (d += 1) {
if (d < 2) {
print("d: {}\n", .{d});
continue;
}
break;
}
}
for enumerate
0..
表示半无限长的序列,拿它和原序列做zip
const std = @import("std");
pub fn main() !void {
const a = [_]i32{ 10, 20, 30, 40, 50 };
// Method 1: Using array indices
for (a, 0..) |ele, index| {
std.debug.print("Index: {}, Element: {}\n", .{ index, ele });
}
}
ArrayList
const std = @import("std");
const expect = std.testing.expect;
const eql = std.mem.eql;
const ArrayList = std.ArrayList;
const test_allocator = std.heap.page_allocator;
pub fn main() !void {
var list = ArrayList(u8).init(test_allocator);
defer list.deinit();
try list.append('H');
try list.append('e');
try list.append('l');
try list.append('l');
try list.append('o');
try list.appendSlice(" World!");
try expect(eql(u8, list.items, "Hello World!"));
}
Arrays
const std = @import("std");
const print = std.debug.print;
pub fn main() !void {
const a = [3]i32{ 1, 2, 3 };
const b = [_]i32{ 4, 5, 6 };
const c: [3]i32 = .{ 7, 8, 9 };
var d: [3]i32 = undefined;
d[0] = 10;
d[1] = 11;
d[2] = 12;
print("len: {}\n", .{c.len});
print("repeat: {any}\n", .{a ** 2});
print("concat: {any}\n", .{a ++ b});
for (d) |elem| {
print("elem: {}\n", .{elem});
}
//var array1: [8]u8 = .{ 1,2,3,4,5,6,7,8 };
//var array2d: [2][4]u8 = .{ .{1,2,3,4}, .{5,6,7,8} };
//var array3d: [2][2][2]u8 = .{ .{ .{1,2}, .{3,4} }, .{ .{5,6}, .{7,8} } };
const mat4x4 = [4][4]f32{
[_]f32{ 1.0, 0.0, 0.0, 0.0 },
[_]f32{ 0.0, 1.0, 0.0, 1.0 },
[_]f32{ 0.0, 0.0, 1.0, 0.0 },
[_]f32{ 0.0, 0.0, 0.0, 1.0 },
};
for (mat4x4) |row| {
for(row) |elem| {
print("elem: {}\n", .{elem});
}
}
}
字符的ASCII码
const std = @import("std");
pub fn main() !void {
const N: usize = 4;
const perms = [_][]const u8{
"ABCD", "CABD", "ACDB", "DACB", "BCDA", "ACBD", "ADCB", "CDAB",
"DABC", "BCAD", "CADB", "CDBA", "CBAD", "ABDC", "ADBC", "BDCA",
"DCBA", "BACD", "BADC", "BDAC", "CBDA", "DBCA", "DCAB",
};
// Calculate n = (N-1)!, the expected number of occurrences
var n: usize = 1;
var i: usize = 1;
while (i < N) : (i += 1) {
n *= i;
}
var miss: [N]u8 = undefined;
var stdout = std.io.getStdOut().writer();
i = 0;
while (i < N) : (i += 1) {
var cnt = [_]usize{0} ** N;
// Count how many times each letter occurs at position i
for (perms) |perm| {
const position = perm[i] - 'A';
cnt[position] += 1;
}
// Find letter not occurring (N-1)! times - that's the missing one
var j: usize = 0;
while (j < N and cnt[j] == n) : (j += 1) {}
miss[i] = @as(u8, @intCast(j)) + 'A';
}
try stdout.print("Missing: {s}\n", .{miss});
}
为什么zig 0.14.0中不能使用std.mem.copy
error: root source file struct 'mem' has no member named 'copy'
pub fn main() !void {
const allocator = std.heap.page_allocator;
const a = [_]i32{ 9, 8, 7, 6, 5, 0, 1, 2, 3, 4 };
var n: usize = 0;
while (n < a.len) : (n += 1) {
var b = try allocator.alloc(i32, a.len);
defer allocator.free(b);
std.mem.copy(i32, b, &a);
nthElement(i32, b, n);
const stdout = std.io.getStdOut().writer();
try stdout.print("n = {}, nth element = {}\n", .{ n + 1, b[n] });
}
}
改成逐个元素赋值的形式:
pub fn main() !void {
const allocator = std.heap.page_allocator;
const a = [_]i32{ 9, 8, 7, 6, 5, 0, 1, 2, 3, 4 };
var n: usize = 0;
while (n < a.len) : (n += 1) {
var b = try allocator.alloc(i32, a.len);
defer allocator.free(b);
// Copy elements one by one instead of using mem.copy
for (0..a.len) |i| {
b[i] = a[i];
}
nthElement(i32, b, n);
const stdout = std.io.getStdOut().writer();
try stdout.print("n = {}, nth element = {}\n", .{ n + 1, b[n] });
}
}
zig 0.14.1中std.fs.openFileAbsolute读取文件/proc/stat
并处理,显示Linux CPU utilization
const std = @import("std");
const print = std.debug.print;
const ArrayList = std.ArrayList;
const Allocator = std.mem.Allocator;
const CpuUtilization = struct {
idle: f64,
not_idle: f64,
};
const ParseError = error{
InvalidFormat,
ZeroTotal,
ParseIntError,
};
const AppError = ParseError || std.fs.File.OpenError || std.fs.File.ReadError || std.mem.Allocator.Error;
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
run(allocator) catch |err| {
print("Error: {}\n", .{err});
return;
};
}
fn run(allocator: Allocator) !void {
const proc_stat_line = try procStat(allocator);
defer allocator.free(proc_stat_line);
const percentages = try parseUtilization(proc_stat_line);
print("{s:<10} {d:.2}%\n", .{ "idle", percentages.idle * 100.0 });
print("{s:<10} {d:.2}%\n", .{ "not-idle", percentages.not_idle * 100.0 });
}
fn procStat(allocator: Allocator) ![]u8 {
const file = try std.fs.openFileAbsolute("/proc/stat", .{});
defer file.close();
const data = try file.readToEndAlloc(allocator, 4096);
defer allocator.free(data);
// Find the first line (up to newline)
var lines = std.mem.splitSequence(u8, data, "\n");
const first_line = lines.next() orelse "";
return try allocator.dupe(u8, first_line);
}
/// Parse the /proc/stat line to extract CPU utilization percentages
///
/// Arguments:
/// * `line` - The first line from /proc/stat
///
/// Returns:
/// A struct containing idle and not_idle percentages
fn parseUtilization(line: []const u8) !CpuUtilization {
// Remove "cpu " prefix and trim whitespace
const values_str = if (std.mem.startsWith(u8, line, "cpu "))
std.mem.trimLeft(u8, line[4..], " ")
else
std.mem.trimLeft(u8, line, " ");
var total: u64 = 0;
var idle: u64 = 0;
var iter = std.mem.splitSequence(u8, values_str, " ");
var index: usize = 0;
while (iter.next()) |value_str| {
if (value_str.len == 0) continue;
const num = std.fmt.parseInt(u64, value_str, 10) catch |err| switch (err) {
error.InvalidCharacter, error.Overflow => return ParseError.ParseIntError,
};
if (index == 3) {
idle = num;
}
total += num;
index += 1;
}
if (total == 0) {
return ParseError.ZeroTotal;
}
const idle_percentage = @as(f64, @floatFromInt(idle)) / @as(f64, @floatFromInt(total));
const not_idle_percentage = 1.0 - idle_percentage;
return CpuUtilization{
.idle = idle_percentage,
.not_idle = not_idle_percentage,
};
}
zig 0.14.0中split函数被废弃了
//pub const split = @compileError("deprecated; use splitSequence, splitAny, or splitScalar");
pub fn solve(self: *NSolver, puzz: []const u8, max_wid: usize) ![][]const u8 {
// Split the input string into tokens
var tokens = std.ArrayList([]const u8).init(self.allocator);
defer tokens.deinit();
var iter = std.mem.splitSequence(u8, puzz, " ");
while (iter.next()) |tok| {
if (tok.len == 0) continue; // Skip empty tokens
try tokens.append(tok);
}
if (tokens.items.len == 0) return &[_][]const u8{};
self.wid = max_wid;
self.hei = tokens.items.len / max_wid;
self.max = tokens.items.len;
const len = self.wid * self.hei;
// allocate and zero‐initialize the board
self.arr = try self.allocator.alloc(Node, len);
for (self.arr) |*n| {
n.* = Node{ .val = 0, .neighbors = 0 };
}
// parse the input tokens into our board
var c: usize = 0;
for (tokens.items) |tok| {
if (std.mem.eql(u8, tok, "*")) {
self.max -= 1;
self.arr[c].val = -1;
} else if (std.mem.eql(u8, tok, ".")) {
// leave . as 0 for "empty"
self.arr[c].val = 0;
} else {
// parse a pre‐filled number
const v = try std.fmt.parseInt(isize, tok, 10);
self.arr[c].val = v;
}
c += 1;
}
// run the backtracking search
self.solveIt();
// Create result array to hold the modified tokens
var result = try self.allocator.alloc([]const u8, tokens.items.len);
// write back any "." slots with zero‐padded numbers
c = 0;
for (tokens.items, 0..) |tok, i| {
if (std.mem.eql(u8, tok, ".")) {
const v = self.arr[c].val;
// format as two digits, zero-pad
result[i] = try std.fmt.allocPrint(self.allocator, "{d:0>2}", .{v});
} else {
// For non-modified tokens, just copy the reference
result[i] = tok;
}
c += 1;
}
return result;
}
实战——变长二维数组
下面这份Zig代码我写了一个多小时。
const std = @import("std");
const allocator = std.heap.page_allocator;
pub fn main() !void {
const result = try generateSequence(5, 13);
for (result) |item| {
std.debug.print("{}", .{item});
}
std.debug.print("\n", .{});
}
fn generateSequence(_k: i32, _n: i32) ![]i32 {
var k=_k; var n=_n;
var s = std.ArrayList(std.ArrayList(i32)).init(allocator);
for (0..@as(usize, @intCast(n))) |i| {
var innerList = std.ArrayList(i32).init(allocator);
if (i < k) {
try innerList.append(1);
} else {
try innerList.append(0);
}
try s.append(innerList);
}
var d:i32 = n-k;
n = @max(k, d);
k = @min(k, d);
var z = d;
while (z > 0 or k > 1) {
for (0..@as(usize, @intCast(k))) |i| {
var lastList = s.items[s.items.len - 1 - i];
for (lastList.items) |item| {
try s.items[i].append(item);
}
}
s.shrinkRetainingCapacity(s.items.len - @as(usize, @intCast(k)));
z -= k;
d = n - k;
n = @max(k, d);
k = @min(k, d);
}
var result = std.ArrayList(i32).init(allocator);
for (s.items) |sublist| {
for (sublist.items) |item| {
try result.append(item);
}
}
return result.toOwnedSlice();
}
实战——算一下组合数
const std = @import("std");
/// Compute the integer power base^exp, with exp ≥ 0.
/// If exp < 0 in your use case, you’ll need custom handling for negative exponents.
fn powInt(base: i128, exp: i128) i128 {
var result: i128 = 1;
var i: i128 = 0;
while (i < exp) : (i += 1) {
result *= base;
}
return result;
}
/// Compute the binomial coefficient C(n, k) = n! / (k! * (n-k)!).
/// Returns 0 if n or k are negative or if k > n.
fn comb(n: i32, k: i32) i128 {
if (k < 0 or n < 0 or k > n) {
return 0;
}
var result: i128 = 1;
var numerator: i128 = @as(i128, n);
var denominator: i128 = 1;
// Only compute up to k_capped = min(k, n-k)
const k_capped = if (k < (n - k)) k else (n - k);
var i: i32 = 0;
while (i < k_capped) : (i += 1) {
result = @divTrunc( (result * numerator) , denominator); // @divExact @divFloor @divTrunc
numerator -= 1;
denominator += 1;
}
return result;
}
/// Translated function f from Python:
/// f = lambda n,v,x,y,z: C(n,v)*C(n-v,x-v)*C(n-x,y-v)*C(n-x-y+v,z-v)*4^(n-x-y-z+2*v)
fn f(n: i32, v: i32, x: i32, y: i32, z: i32) i128 {
const part1 = comb(n, v);
const part2 = comb(n - v, x - v);
const part3 = comb(n - x, y - v);
const part4 = comb(n - x - y + v, z - v);
// exponent = (n - x - y - z + 2*v)
const exponent = @as(i128, n - x - y - z + (2 * v));
// If exponent < 0, it effectively yields 0 for integer purposes, but here we keep it simple:
if (exponent < 0) return 0;
const part5 = powInt(4, exponent);
return part1 * part2 * part3 * part4 * part5;
}
pub fn main() !void {
// A small inline "assert_eq" for i128 comparisons
const assert_eq = struct {
pub fn check(actual: i128, expected: i128) void {
if (actual != expected) {
std.debug.print(
"Assertion failed! Expected {}, got {}.\n",
.{ expected, actual }
);
@panic("assertion failed");
}
}
};
// Tests matching the Rust and Python version
assert_eq.check(f(2, 0, 1, 1, 1), 0);
assert_eq.check(f(2, 1, 1, 1, 1), 8);
assert_eq.check(f(2, 2, 1, 1, 1), 0);
assert_eq.check(f(8, 0, 2, 3, 3), 560);
assert_eq.check(f(8, 1, 2, 3, 3), 80640);
assert_eq.check(f(8, 2, 2, 3, 3), 215040);
assert_eq.check(f(8, 3, 2, 3, 3), 0);
std.debug.print("All tests passed!\n", .{});
}
实战——考虑溢出的整数加法
uint32_t temp = std::rotl(a, 5) + f + e + k + values[j];
e = d; d = c; c = std::rotl(b, 30); b = a; a = temp;
// Use wrapping add to prevent integer overflow panics
const rotated_a = std.math.rotl(u32, a, 5);
const temp = @addWithOverflow(rotated_a, f)[0];
const temp2 = @addWithOverflow(temp, e)[0];
const temp3 = @addWithOverflow(temp2, k)[0];
const temp4 = @addWithOverflow(temp3, values[j])[0];
e = d;
d = c;
c = std.math.rotl(u32, b, 30);
b = a;
a = temp4;
实战——Zig 0.14.0中使用随机数
Cipolla's algorithm, Solve x² ≡ n (mod p)
const std = @import("std");
const math = std.math;
// Random number generator in range [min, max]
fn rangeRandom(min: u64, max: u64) u64 {
if (min > max) return min;
// seed it however you like, here we just use the timestamp:
var prng = std.Random.DefaultPrng.init(@as(u64, @intCast(std.time.milliTimestamp())));
// directly pull a number in [min, max]
return prng.random().intRangeAtMost(u64, min, max);
}
实战——使用Zig实现一些图论算法
Hamiltonian Cycle via Chvátal Closure
There is no std.BTreeSet in Zig's standard library
You should replace std.BTreeSet with std.ArrayList since BTreeSet doesn't exist in Zig's standard library