如何实现调用缓存(Memoization)

时间:2014-06-04 23:07:28

标签: dart memoization dart-mirrors

我希望以非侵入方式使用元数据注释实现调用缓存(memoization)。

希望它会像这样工作:

class A{
  @Cached
  foo(msg) {
    return msg;
  }
}

void main() {
  @Cached
  var foo = ()=>"hello";
}

只能使用dart:mirrors吗?

2 个答案:

答案 0 :(得分:4)

我刚才写了一篇关于这个主题的博客文章。复制到这里太长了,所以这里有链接:

http://dartery.blogspot.com/2012/09/memoizing-functions-in-dart.html

结果是你可以编写更高阶的记忆功能,但是由于Dart缺乏灵活的args功能,它们的普遍性受到限制。另外,如果你想使用带有递归函数的动态编程,你需要记住你的memoization函数 - 它需要将自己作为一个参数,所以你可以传入memoized版本。

答案 1 :(得分:0)

我目前的解决方案允许:

class B {
  @CachedCallName(#cachedBaz)
  baz() => print("first call to baz");
}
class A extends B with CacheableCalls {
  @CachedCallName(#foo)
  _foo(msg) {
    print("first call with: $msg");
    return msg + msg;
  }
}
void main() {
  A a = new A();
  print(a.foo(21));
  print(a.foo(21));
  a.cachedBaz();
  print(a.foo(22));
  a.cachedBaz();
}

输出:

  

首先打电话:21
  42个
  42个
  第一次打电话给巴兹   第一次打电话:22
  44

缺陷:
  - 无法使用实际名称缓存方法   - 可以扩展集合视图,但不能缓存operator []之类的现有运营商   - 无法缓存功能。

完整来源:

@MirrorsUsed(metaTargets: CachedCallName)
import 'dart:mirrors';

class CachedCallName {
  final Symbol name;
  const CachedCallName(this.name);
}
@proxy
class CacheableCalls {
  Map _cache = new Map();
  dynamic _chacheInvoke(InstanceMirror thisMirror, Symbol
      methodName, Invocation invocation) {
    String key = "$methodName${invocation.positionalArguments}"
        "${invocation.namedArguments}";
    if (_cache.containsKey(key)) {
      return _cache[key];
    } else {
      InstanceMirror resultMirror = thisMirror.invoke(methodName,
          invocation.positionalArguments, invocation.namedArguments);
      _cache[key] = resultMirror.reflectee;
      return resultMirror.reflectee;
    }
  }
  dynamic noSuchMethod(Invocation invocation) {
    bool isFound = false;
    var result;
    Symbol called = invocation.memberName;
    InstanceMirror instanceMirror = reflect(this);
    ClassMirror classMirror = instanceMirror.type;
    classMirror.instanceMembers.forEach((Symbol name, MethodMirror mm) {
      mm.metadata.forEach((InstanceMirror im) {
        if (im.reflectee is CachedCallName) {
          if (im.reflectee.name == called) {
            isFound = true;
            result = _chacheInvoke(instanceMirror, name, invocation);
          }
        }
      });
    });

    if (isFound) {
      return result;
    } else {
      throw new NoSuchMethodError(this, called,
          invocation.positionalArguments, invocation.namedArguments);
    }
  }
}
class B {
  @CachedCallName(#cachedBaz)
  baz() => print("first call to baz");
}
class A extends B with CacheableCalls {
  @CachedCallName(#foo)
  _foo(msg) {
    print("first call with: $msg");
    return msg + msg;
  }
}
void main() {
  A a = new A();
  print(a.foo(21));
  print(a.foo(21));
  a.cachedBaz();
  print(a.foo(22));
  a.cachedBaz();
}