maa*_*nus 6 java tree recursion caching guava
我完全重写了这个问题,因为原来的问题无法解决.为了保持简单,我使用Fibonacci数字作为玩具示例.
在平凡的递归缓存的计算一个很长的堆栈跟踪结束,正如预期.这就是为什么我想有这样一个抽象类IterativeLoadingCache,我可以扩展喜欢这里的类似
@Override
protected Integer computeNonRecursivelly(Integer key) {
final Integer x1 = getOrEnqueue(key-1);
final Integer x2 = getOrEnqueue(key-2);
if (x1==null) return null;
if (x2==null) return null;
return x1+x2;
}
Run Code Online (Sandbox Code Playgroud)
并且它将在不使用递归的情况下处理所有缓存和计算.
我真的不是在寻找斐波纳契数的有效计算.我需要一些允许使用缓存和递归函数的东西,其中递归深度可以任意高.
我已经有了一种解决方案,但它效率很低而且非常难看,所以我希望得到一些好的建议.如果其他人需要它或者已经实现它,我也很好奇.
由于您重写了问题,因此这是一个新答案。
首先,在我看来,您的实现computeNonRecursivelly仍然是递归的,因为getOrEnqueue调用了它。
我认为您不能Cache直接使用 a ,因为您在计算中需要有两个步骤:一个是声明所需值的依赖关系,另一个是在满足依赖关系后进行计算。不过,只有当您从不存在循环依赖项时,它才有效(这与递归中的要求相同)。
这样,您可以对缓存中尚未存在的依赖项(及其依赖项等)进行排队,然后以正确的顺序计算它们。大致如下:
public abstract class TwoStepCacheLoader<K, V> extends CacheLoader<K, V> {
public abstract Set<K> getDependencies(K key);
}
public class TwoStepCache<K, V> extends ForwardingLoadingCache<K, V> {
private final TwoStepCacheLoader<K, V> loader;
private LoadingCache<K, V> cache;
public TwoStepCache(TwoStepCacheLoader<K, V> loader) {
this.loader = loader;
cache = CacheBuilder.newBuilder().build(loader);
}
@Override
public V get(K key)
throws ExecutionException {
V value = cache.getIfPresent(key);
if (value != null) {
return value;
}
Deque<K> toCompute = getDependenciesToCompute(key);
return computeDependencies(toCompute);
}
private Deque<K> getDependenciesToCompute(K key) {
Set<K> seen = Sets.newHashSet(key);
Deque<K> dependencies = new ArrayDeque<K>(seen), toCompute = new ArrayDeque<K>(seen);
do {
for (K dependency : loader.getDependencies(dependencies.remove())) {
if (seen.add(dependency) && // Deduplication in the dependencies
cache.getIfPresent(dependency) == null) {
// We need to compute it.
toCompute.push(dependency);
// We also need its dependencies.
dependencies.add(dependency);
}
}
} while (!dependencies.isEmpty());
return toCompute;
}
private V computeDependencies(Deque<K> toCompute)
throws ExecutionException {
V value;
do {
value = cache.get(toCompute.pop());
} while (!toCompute.isEmpty());
// The last computed value is for our key.
return value;
}
@Override
public V getUnchecked(K key) {
try {
return get(key);
} catch (ExecutionException e) {
throw new UncheckedExecutionException(e.getCause());
}
}
@Override
protected LoadingCache<K, V> delegate() {
return cache;
}
}
Run Code Online (Sandbox Code Playgroud)
现在您可以实现TwoStepCacheLoader安全地调用缓存的方法:
public class Fibonacci {
private LoadingCache<Integer, Integer> cache = new TwoStepCache<Integer, Integer>(new FibonacciCacheLoader());
public int fibonacci(int n) {
return cache.getUnchecked(n);
}
private class FibonacciCacheLoader extends TwoStepCacheLoader<Integer, Integer> {
@Override
public Set<Integer> getDependencies(Integer key) {
if (key <= 1) {
return ImmutableSet.of();
}
return ImmutableSet.of(key - 2, key - 1);
}
@Override
public Integer load(Integer key)
throws Exception {
if (key <= 1) {
return 1;
}
return cache.get(key - 2) + cache.get(key - 1);
}
}
}
Run Code Online (Sandbox Code Playgroud)
我已经对其进行了单元测试,它似乎运行正确。
编辑:更改了实现,以允许Expression在多个线程中作为参数传递相同的计算时进行单个计算。
不要使用 a LoadingCache,只需将结果缓存在eval(一旦修改为使用迭代而不是递归):
public Node eval(final Expression e) {
if (e==null) return null;
return cache.get(e, new Callable<Node>() {
@Override
public Node call() {
final Node n0 = eval(leftExpression(e));
final Node n1 = eval(rightExpression(e));
return new Node(n0, n1);
}
});
}
private final Cache<Expression, Node> cache
= CacheBuilder.newBuilder().build();
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
1486 次 |
| 最近记录: |