我认为这可能与冒泡排序本身无关,而更多地与我对 Zig“风格”的理解有关。
我想在 Zig 中实现冒泡排序,以便它返回一个新数组。这种设置将使编写简单的单行测试变得更加容易。
我不能这么做:
pub fn bubbleSort(arr: []const u8,) []const u8 {
var arrSorted = [_]u8{0} ** arr.len;
std.mem.copyForwards(u8, &arrSorted, arr);
...
}
因为 zig 会指出 arr.len 是一个未知的编译时属性
所以我最终这样做了:
const std = @import("std");
/// O(n^2)
pub fn bubbleSort(arr: []const u8, comptime len: u8) []const u8 {
var arrSorted = [_]u8{0} ** len;
std.mem.copyForwards(u8, &arrSorted, arr);
for (0..arrSorted.len) |i| {
for (0..arrSorted.len - i - 1) |j| {
if (arrSorted[j] > arrSorted[j + 1]) {
const tmp = arrSorted[j];
arrSorted[j] = arrSorted[j + 1];
arrSorted[j + 1] = tmp;
}
}
}
return &arrSorted;
}
test "bubble sort" {
try std.testing.expectEqualSlices(u8, &[_]u8{ 1, 4, 7, 8, 9, 102 }, bubbleSort(&[_]u8{ 1, 102, 7, 4, 8, 9 }, 6));
try std.testing.expectEqualSlices(u8, &[_]u8{ 1, 4, 7, 8, 9, 102 }, bubbleSort(&[_]u8{ 102, 7, 4, 1, 8, 9 }, 6));
try std.testing.expectEqualSlices(u8, &[_]u8{ 1, 4, 7, 8, 9, 102 }, bubbleSort(&[_]u8{ 1, 102, 7, 4, 9, 8 }, 6));
try std.testing.expectEqualSlices(u8, &[_]u8{ 1, 4, 7, 8, 9, 102 }, bubbleSort(&[_]u8{ 9, 1, 102, 7, 4, 8 }, 6));
try std.testing.expectEqualSlices(u8, &[_]u8{ 1, 4, 7, 8, 9, 102 }, bubbleSort(&[_]u8{ 1, 7, 4, 8, 9, 102 }, 6));
}
这里,我将数组的长度作为参数传递。通过直接在“纯文本”中指定该值,它在编译时就已知,因此所有这些都有效。
我知道我可以将分配器传递给 bubbleSort 函数,让它为我的数组分配内存,然后推迟在调用代码块中释放该内存。这对于我想要实现的目标来说是正确的方法吗?
还有其他更简单、更快或更优雅的方法吗?
感谢您的阅读,如果标题或帖子不完美,我们深感抱歉——我想不出更好的方法来解释它。
我尝试从函数返回一个新数组,而不需要分配器或 comptime 已知属性,但失败了。
公平地说,我不确定您在测试中拥有漂亮俏皮话的目标是否可以实现。以下是标准库中排序函数的测试方式:std.sort.zig:225
最好的选择是定义一个辅助函数来完成所有繁重的工作,并且您可以多次调用它。
Zig 有以下内存分配选项:
您可能会注意到您的方法:
var arrSorted = [_]u8{0} ** len;
// ...
return &arrSorted;
未在可用选项中列出。这是因为这样做是违法的。这是新手常犯的错误,特别是如果他们有 GC 语言经验的话。内存仅在函数执行时由该函数拥有,一旦退出,该内存将被其他函数重用。根据程序的执行,这可能会产生正确的结果,也可能会产生完全无意义的结果。