आप अपने प्रश्न फिर से लिख दिया के बाद से, यहाँ एक नया जवाब ।
सबसे पहले, यह मुझे लगता है कि computeNonRecursivelly
के कार्यान्वयन की तरह मुझे अभी भी रिकर्सिव है, क्योंकि getOrEnqueue
इसे कॉल करता है।
मुझे नहीं लगता कि आप सीधे Cache
का उपयोग कर सकते हैं, क्योंकि आपको अपनी गणना में 2 कदम रखने की आवश्यकता है: जो वांछित मूल्य के लिए निर्भरता बताता है, और एक निर्भरता मिलने के बाद गणना करता है। यह केवल तभी काम करेगा जब आपके पास चक्रीय निर्भरता न हो, हालांकि (रिकर्सन में यह वही आवश्यकता है)।
इस तरह, आप निर्भरता जो कैश (और उनके निर्भरता, आदि) में पहले से ही नहीं कर रहे हैं कतार फिर उन्हें सही क्रम में गणना कर सकता है। की तर्ज पर कुछ:
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;
}
}
अब आप एक 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);
}
}
}
मैं इस पर एक इकाई परीक्षण चलाने के बाद, इसे सही ढंग से चलाने के लिए लगता है।
आप पर डॉ हाइन्ज़ एम Kabutz 'लेख पर ध्यान दिया है (http://www.javaspecialists.eu/archive/Issue201.html) [कांटा/फाइबोनैचि और Karatsuba के साथ जुड़ें]। उनके कुछ विचार लागू हो सकते हैं। – OldCurmudgeon
@ ओल्डकुरम्यूजियन: अब मेरे पास है, यह दिलचस्प है लेकिन वास्तव में मदद नहीं करता है। – maaartinus
मुझे यह [कैश फिबोनासी उदाहरण] मिला है (http://vladmihalcea.com/2014/03/03/caching-best-practices/) जो लागू होने लगता है। नीचे की ओर "बजाना समय" नामक अनुभाग देखें। ऐसा लगता है कि पिछली गणना संख्या को कैश में रखकर और पुराने मानों को बेदखल कर काम करता है। रिकर्सिव लोड 'लोड' विधि में बयानों के सावधानीपूर्वक आदेश से बचा जाता है।मुझे यकीन नहीं है कि अगर इसे 'खिलौना' उदाहरण से परे बढ़ाया जा सकता है, जैसा कि आपने इसे रखा है। –