在Haskell中,我如何在O(1)的时间复杂度内冻结/解冻可变向量的可变向量?

4
我正在使用来自vector库的MVector来表示图的某些邻接表。这个问题需要对vector包有很好的了解。
我希望我的一些图形函数以可变图形作为输入,因为它们可能会更改它。而其他一些函数应该将不可变的图形作为输入,因为它们不打算更改它。
到目前为止,我拥有的每个图形函数都将可变图形作为输入。这不太好。
我喜欢从这里https://hackage.haskell.org/package/vector-0.12.1.2/docs/Data-Vector-Generic.html中的unsafeFreeze如何允许在O(1)中获取矢量的不可变版本。 我相信,在底层它确实没有做任何其他事情,只是将类型转换。
基本上我希望能够以零成本“不安全地”频繁冻结任何复杂的可变结构,以便我可以将其传递给需要不可变参数的函数。就像在任何常见的编程语言中一样。尽管有“不安全”的前缀,但如果冻结结构在用作不可变参数后立即被丢弃,则我认为这不是不安全的实践。垃圾回收不应发生,因为可变版本未被丢弃,冻结版本应指向与可变版本相同的内存表示。

我遇到的问题是如何冻结视为可变向量的图形的问题。到目前为止,我唯一想出的方法是使用unsafeFreeze在每个可变子向量上调用来生成主向量的新不可变版本。但这在主向量长度为n时是O(n)的。这是不可接受的。

澄清一下,仅冻结主向量是不够的,因为它会给我一个冻结的可变向量的向量。理论上应该有一种以零成本冻结所有内容的方法,因为内部内存表示根本不应更改。这应该只是一个转换。

有没有办法以(非常便宜的)O(1)的方式冻结一个可变向量的可变向量?

我知道这个问题只涉及到vector包。如果您有可变向量的其他包建议,我很乐意听取。

请不要回答“使用不可变结构。结束。” 我的观点是,图形、不可变性和性能通常不搭配。
谢谢。

我需要深入了解这个包并理解unsafeFreeze的工作原理。但是向量代码相当混乱。需要花费一些时间来理解。 - jam
unsafeFreeze引导我去了另一个包。 http://hackage.haskell.org/package/primitive-0.7.0.1/docs/src/Data.Primitive.Array.html#unsafeFreezeArray 但我还不太理解这个充满#的代码。谁给我点踩了,你能告诉我错在哪里吗? - jam
1
unsafeFreezeArray#是编译器原语。其中的#并没有实际意义,它只是名称的一部分。实现在这里。它似乎有两个作用:重写“信息表”(运行时类型信息)和(隐式地)“转换”指针。一个unsafeCoerce可能可以工作(实际上很少有东西使用运行时类型信息),但我会说你不能这样做。使用类型类解决方案来重载操作以使其适用于不可变和可变图形? - HTNW
@HTNW 是的,我最初使用了一个类型类的解决方案,我没有区分可变参数和不可变参数。但是我发现了一种非常好的技巧,使用了单射类型族:type family Mut s (x :: k) = (r :: k) | r -> x。这使我能够重新创建mut模式,就像其他现代语言(如Rust)中看到的那样。现在,我想让我的整个代码库变得漂亮,但老实说,我觉得这个向量包很丑。我将尝试学习如何使用不安全的强制转换。我也认为这可能是一个好主意。 - jam
1个回答

1
抱歉,你做不到。冻结是一次只能处理一个数组的操作。你可以构建一个API,使用可变结构来“表示”不可变结构。根据你的使用方式,可能会遇到一些GC性能下降的问题。
另一个选择是同时使用unsafeFreeze和unsafeThaw。例如,可以看看unorderedContainers中列表如何构建HashMaps的方式。但是你必须小心,因为类型系统无法帮助你记住哪些内容将被改变,哪些不会。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接