【发布时间】:2015-08-26 18:12:31
【问题描述】:
在 Haskell 中,我有一个类似的容器:
data Container a = Container { length :: Int, buffer :: Unboxed.Vector (Int,a) }
这个容器是一棵扁平的树。它的访问器(!) 对向量执行二进制(log(N))搜索,以便找到存储index 的正确存储桶。
(!) :: Container a -> Int -> a
container ! index = ... binary search ...
由于连续访问可能在同一个桶中,因此可以通过以下方式进行优化:
if `index` is on the the last accessed bucket, skip the search
棘手的一点是last accessed bucket 部分。在 JavaScript 中,我只是不纯地修改了容器对象上的隐藏变量。
function read(index,object){
var lastBucket = object.__lastBucket;
// if the last bucket contains index, no need to search
if (contains(object, lastBucket, index))
var bucket = lastBucket;
// if it doesn't
else {
// then we search the bucket
var bucket = searchBucket(index,object);
// And impurely annotate it on the container, so the
// next time we access it we could skip the search.
container.__lastBucket = bucket;
}
return object.buffer[bucket].value;
}
由于这只是一种优化,并且结果与所采用的分支无关,因此我相信它不会破坏引用透明度。在 Haskell 中,如何不纯地修改与运行时值关联的状态?
~
我想到了两种可能的解决方案。
一个全局的、可变的 hashmap 链接指向
lastBucket值的指针,并使用 unsafePerformIO 对其进行写入。但我需要一种方法来获取对象的运行时指针,或者至少是某种唯一 id(如何?)。向
Container、lastBucket :: Int添加一个额外的字段,并在(!)中以某种方式不纯地修改它,并将该字段视为内部字段(因为它显然破坏了引用透明度)。
【问题讨论】:
-
对于第二种可能,你可能想用
lastBucket :: IORef Int代替,用unsafePerformIO来“不纯地修改它”。您的 JS 代码无法处理__lastBucket可能被另一个线程的另一个read()调用修改的事实,因此在if和=处具有不同的值。 -
@Xicò 当然,谢谢!不过,我希望我可以隐藏它,因为它不是 API 的一部分。还有,你说的是真的。但我这里不需要锁,对吗?只需将
__lastBucket存储在开头的变量中即可。 (JS没有线程......)。编辑:更新了 OP。 -
如果您决定使用不安全的技巧,请注意线程。
-
第一个使用全局地图的解决方案需要使用weakmap,否则你会遇到垃圾回收问题。
-
将您的操作提升到
ST...
标签: haskell optimization functional-programming referential-transparency