周末小项目,一个玩具表达式求值器,宿主函数用 D 写,按名字注册。我在注册表里加了一个 memo 缓存(挺得意的,吹了一整周了)。规则是这样的,如果函数是 pure,就不可能有副作用,所以按名字加打印出来的参数做键,缓存结果。
然后我写了 bump,把收到的数组的第一个元素加 1。循环每一步都新建 counter = [0],调用 bump,再打印。我预期每次都是 [1]。实际得到的是 step 0: counter=[1],然后 step 1: counter=[0] 和 step 2: counter=[0]。加 --verbose 能看到第 1、2 步命中了缓存。
tick(不是 pure,改一个全局变量)没问题。twice 也没问题。
我试过的有这些,用 new int[1] 代替 [0],没变化。我怀疑是 register 里的 delegate 吞掉了写入,或者 functionAttributes 在骗人,但把标志打印出来,bump 确实是 pure。编译器一声不吭就接受了。(argparse 在 Phobos 里,所以不是它的问题。)
pure 不就是用来标记可以安全缓存的吗?为什么写入直接被跳过了?
// host.d
pure void bump(int[] a)
{
a[0]++;
}
// cache.d
enum attrs = functionAttributes!fn;
r.cacheable[name] = (attrs & FunctionAttribute.pure_) != 0;
int[] call(ref Registry r, string name, int[] args)
{
if (r.cacheable[name])
{
auto key = name ~ args.to!string;
if (auto p = key in r.memo)
{
r.hits++;
return *p;
}
r.misses++;
auto res = r.fns[name](args);
r.memo[key] = res;
return res;
}
return r.fns[name](args);
}
// app.d
foreach (i; 0 .. opts.steps)
{
auto counter = [0];
reg.call("bump", counter);
writeln("step ", i, ": counter=", counter);
}
从英语翻译·查看原文Weekend project: a toy expression evaluator with host functions written in D, registered by name. I added a memo cache in the registry (pretty proud of it, I've been bragging about it all week). The rule: if a function is pure, it can't have side effects, so cache the result keyed on name plus printed args.
Then I wrote bump, which increments the first element of the array it gets. Each loop step makes a fresh counter = [0], calls bump, prints. I expected [1] every time. I get step 0: counter=[1], then step 1: counter=[0] and step 2: counter=[0]. With --verbose there's a cache hit on steps 1 and 2.
tick (not pure, bumps a global) is fine. twice is fine too.
Things I tried: new int[1] instead of [0], no change. I figured the delegate in register was eating the write, or functionAttributes was lying, but printing the flag shows bump really is pure. The compiler took it without a peep. (argparse is in Phobos, so that's not it.)
Isn't pure supposed to be the safe-to-cache marker? Why is the write just skipped?
// host.d
pure void bump(int[] a)
{
a[0]++;
}
// cache.d
enum attrs = functionAttributes!fn;
r.cacheable[name] = (attrs & FunctionAttribute.pure_) != 0;
int[] call(ref Registry r, string name, int[] args)
{
if (r.cacheable[name])
{
auto key = name ~ args.to!string;
if (auto p = key in r.memo)
{
r.hits++;
return *p;
}
r.misses++;
auto res = r.fns[name](args);
r.memo[key] = res;
return res;
}
return r.fns[name](args);
}
// app.d
foreach (i; 0 .. opts.steps)
{
auto counter = [0];
reg.call("bump", counter);
writeln("step ", i, ": counter=", counter);
}