当执行简单的变量赋值更改函数行为时,我在 Zig 中使用 ArrayList 是否错误?

2023-12-22

我今年一直在做《Advent of Code》,以学习 Zig,我在第 5 天发现了一些让我真正困惑的东西。那么:我猜,2022 年代码降临第 5 天会有轻微剧透吗?

我决定在第 5 天将我的解决方案实现为 U8 的 ArrayList 的 ArrayList,最终效果良好。我的完整解决方案文件是here https://github.com/daogilvie/aoc2022/blob/main/src/day5.zig(可能非常不习惯 Zig,但我们都必须从某个地方开始)。

作为我的解决方案的一部分,我有一个函数,我称之为 moveCrates,它位于一个包装数组列表的数组列表的结构上。

结构声明的相关部分如下所示:

const BunchOfStacks = struct {
    stacks: ArrayList(ArrayList(u8)),
    ...

这个函数是here https://github.com/daogilvie/aoc2022/blob/d82304dbf855eb6f877e7d37e3b666206fd169f1/src/day5.zig#L36-L41,看起来像这样:

    fn moveCrates(self: *BunchOfStacks, amount: usize, source: usize, dest: usize) !void {
        const source_height = self.stacks.items[source - 1].items.len;
        const crate_slice = self.stacks.items[source - 1].items[(source_height - amount)..];
        try self.stacks.items[dest - 1].appendSlice(crate_slice);
        self.stacks.items[source - 1].shrinkRetainingCapacity(source_height - amount);
    }

您可以注意到,我通过非常详细的参考文献引用了源列表 3 次self.stacks.items[source - 1]。这不是我第一次编写这个函数的方式。我首先是这样写的:

    fn moveCrates(self: *BunchOfStacks, amount: usize, source: usize, dest: usize) !void {
        var source_list: ArrayList(u8) = self.stacks.items[source - 1];
        const source_height = source_list.items.len;
        const crate_slice = source_list.items[(source_height - amount)..];
        try self.stacks.items[dest - 1].appendSlice(crate_slice);
        source_list.shrinkRetainingCapacity(source_height - amount);
    }

但是第二种形式,为了方便起见,我创建了一个局部变量,但没有给出正确的结果!它编译得很好,但似乎总是指向source_list到同一个内部ArrayList(u8)(以它第一个选择的为准)无论其值是多少source是。这意味着测试示例产生不正确的输出。

该函数在循环内调用,如下所示:

    while (instructions.next()) |_| {
        // First part is the verb, this is always "move" so skip it
        // Get the amount next
        const amount: usize = try std.fmt.parseInt(usize, instructions.next().?, 10);
        // now skip _from_
        _ = instructions.next();
        // Now get source
        const source: usize = try std.fmt.parseInt(usize, instructions.next().?, 10);
        // Now skip _to_
        _ = instructions.next();
        // Now get dest
        const dest: usize = try std.fmt.parseInt(usize, instructions.next().?, 10);

        var crates_moved: usize = 0;
        while (crates_moved < amount) : (crates_moved += 1) {
            try stacks_part1.moveCrates(1, source, dest);
        }
        try stacks_part2.moveCrates(amount, source, dest);
    }

最终,如您所见,我刚刚避免在函数中进行变量赋值,并且这通过了测试(和难题)。

我已经检查了 zig 存储库中可能相关的问题,但找不到任何明显的东西(使用的搜索是this https://github.com/ziglang/zig/issues?q=is%3Aissue+is%3Aopen+assignment+loop)。我查看了 StackOverflow,发现这个问题 https://stackoverflow.com/questions/66630797/how-to-create-2d-arrays-of-containers-in-zig,这确实与我的问题有一些相似之处(指针在 while 循环中似乎有点混乱),但它并不相同。

我已经搜索了有关循环和赋值的 Zig 文档,site https://ziglang.org/documentation/master/,但没有看到任何专门指出此行为的内容。 我假设我要么完全误解了某些东西(或者错过了一些没有很好记录的东西),要么这是一个错误——毕竟,该语言正在大量活跃的开发中。

我期望像我执行的那样的作业应该按预期工作——作为一个简单的速记,以避免必须写出重复的内容self.stacks.items[source - 1],所以我希望这是我做错的事情。 Zig 版本是v0.11.0-dev.537+36da3000c


数组列表存储items作为切片(指向第一项的指针加上长度)。 当你这样做时

var source_list: ArrayList(u8) = self.stacks.items[source - 1];

您制作了数组列表的浅表副本。您可能会对某些高级语言的了解感到困惑,在这些语言中,这会复制对数组列表对象的引用,但在 Zig 中并非如此。在 Zig 中,一切都是“价值对象”。

你打电话时shrinkRetainingCapacity它改变了长度属性items切片,但更改是对数组列表的本地副本进行的。存储在另一个数组列表中的“真实”数组列表不受影响。

TL;DR 您需要使用pointer https://ziglang.org/documentation/0.10.0/#Pointers:

var source_list: *ArrayList(u8) = &self.stacks.items[source - 1];

这修复了失败的测试。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

当执行简单的变量赋值更改函数行为时,我在 Zig 中使用 ArrayList 是否错误? 的相关文章

随机推荐