Como implementar o cache de chamadas (Memoização)
-
21-12-2019 - |
Pergunta
Quero implementar o cache de chamadas (memoização) de forma não intrusiva com as anotações de metadados.
Esperançosamente, funcionará assim:
class A{
@Cached
foo(msg) {
return msg;
}
}
void main() {
@Cached
var foo = ()=>"hello";
}
Isso pode ser alcançado apenas com dart:mirrors
?
Solução
Escrevi uma postagem inteira no blog sobre esse assunto há algum tempo.É muito longo para copiar aqui, então aqui está o link:
http://dartery.blogspot.com/2012/09/memoizing-functions-in-dart.html
O resultado é que você pode escrever funções de memorização de ordem superior, mas elas são limitadas em geral pela falta de funções args flexíveis do Dart.Além disso, se você quiser usar programação dinâmica com funções recursivas, você precisa escrever sua função tendo em mente a memoização - ela precisa se considerar um argumento, para que você possa passar a versão memoizada.
Outras dicas
Minha solução atual permite:
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();
}
Saída:
primeira chamada com:21
42
42
primeira chamada para baz
primeira chamada com:22
44
Imperfeições:
- não é possível armazenar métodos em cache com seus nomes reais.
- pode estender a visualização da coleção, mas não pode armazenar em cache operadores existentes como operator []
- não é possível armazenar funções em cache.
Fonte completa:
@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();
}