在 JavaScript 中,记住像Fibonacci这样的函数非常简单:
// In JavaScript
var fibonacci = (function () {
var cache = {}; // cache for future calculations
return function (num) {
if (num < 0) throw new Error('Negative numbers not allowed');
if (num === 0) return 0;
if (num === 1) return 1;
cache[num] = cache[num] || fibonacci(num - 1) + fibonacci(num - 2);
return cache[num];
};
})();
console.log( fibonacci(5) ); // results in 5
console.dir( fibonacci ); // you can inspect the closure scope and see that the cache object saves the values for future use
我正在尝试理解如何在 Ruby 中做类似的事情,不幸的是,我唯一能想到的就是创建一个类并将缓存存储为类变量:
# In Ruby
class Placeholder
@@cache = {}
def fibonacci(num)
raise 'Negative numbers not allowed' if num < 0
return 0 if num == 0
return 1 if num == 1
@@cache[num] ||= fibonacci(num - 1) + fibonacci(num - 2)
end
end
example = Placeholder.new
puts example.fibonacci(5) # results in 5
我不喜欢这样的事实是,当我不打算创建Placeholder
的实例时,我正在创建一个类结构。相反,我只是这样做,因为我想将状态保存在Ruby类变量中。理想情况下,如果我能够创建module
并拥有module
变量,那么这至少可以解决我使用基于class
的解决方案进行实例化的“问题”。 在Ruby中这样做的最佳建议是什么?
根据@ meagar的评论进行更新:
@meagar,你在暗示这样的事情吗?
class Placeholder
attr_reader :cache
def initialize
@cache = {}
end
def fibonacci(num)
raise 'Negative numbers not allowed' if num < 0
return 0 if num == 0
return 1 if num == 1
@cache[num] ||= fibonacci(num - 1) + fibonacci(num - 2)
end
end
FibonacciCalculator = Placeholder.new
puts FibonacciCalculator.fibonacci(5) # results in 5
我已经比我最初的Ruby解决方案更喜欢这个了,尽管让Placeholder类仍然以错误的方式让我感到厌烦。
答案 0 :(得分:4)
当您不需要实例时,可以使用单{{1>} {/ 1>}:
Module
请注意,对于缓存,module Fibonacci
@cache = {}
def self.series(num)
if @cache[num] then return @cache[num]; end
if num < 0 then raise 'Negative numbers not allowed'; end
if num == 0 then return 0; end
if num == 1 then return 1; end
@cache[num] = series(num - 1) + series(num - 2)
end
end
puts Fibonacci.series(5) # results in 5
模块上的实例变量与类变量一样有效(对于某些扩展用途,它可能更好)。它的工作原理是因为模块Fibonacci是Fibonacci
的一个实例 - 它在这方面与任何其他实例变量相同。
答案 1 :(得分:3)
您的ECMAScript版本的字面翻译将是:
fibonacci = -> {
cache = {} # cache for future calculations
-> num {
raise ArgumentError, 'Negative numbers not allowed' if (num < 0)
return 0 if num.zero?
return 1 if num == 1
cache[num] ||= fibonacci.(num - 1) + fibonacci.(num - 2)
}
}.()
fibonacci.(5)
# => 5
fibonacci.binding.local_variable_get(:cache)
# => {2=>1, 3=>2, 4=>3, 5=>5}
顺便说一下,我们可以做一些简化:如果0
是num
则返回0
,而1
则返回num
是1
,如果num
是num
或0
(或1
),我们就可以返回num <= 1
。事实上,我们可以通过简单地使用cache
和0
的值初始化1
来完全摆脱整个条件。此外,缓存只能是Array
,因为索引只是Integer
s的连续范围:
fibonacci = -> {
cache = [0, 1] # cache for future calculations
-> num {
raise ArgumentError, 'Negative numbers not allowed' if (num < 0)
cache[num] ||= fibonacci.(num - 1) + fibonacci.(num - 2)
}
}.()
有趣的是,如果我们在现代ECMAScript中写出来,那么这种关系就变得很明显了:
const fibonacci = (() => {
const cache = [0, 1, 1]; // cache for future calculations
return num => {
if (num < 0) throw new Error('Negative numbers not allowed');
return cache[num] = cache[num] || fibonacci(num - 1) + fibonacci(num - 2);
};
})();
console.log(fibonacci(5));
&#13;
在旧学校ECMAScript中会是这样的:
var fibonacci = function () {
var cache = [0, 1, 1]; // cache for future calculations
return function (num) {
if (num < 0) throw new Error('Negative numbers not allowed');
return cache[num] = cache[num] || fibonacci(num - 1) + fibonacci(num - 2);
};
}();
console.log(fibonacci(5));
&#13;
答案 2 :(得分:2)
...当我不打算创建占位符
的实例时,我是一个类结构
嗯,这是你的问题。
Ruby是一种面向对象的语言。您不能拥有不属于对象的函数。 在对象上调用每个方法。
您只需创建Placeholder
的实例(并为其指定适当的名称,如FibonacciCalculator
),并使您的缓存成为该对象的简单实例变量。
答案 3 :(得分:2)
您还可以使用闭包来存储缓存,这类似于javascript的操作方式。
def memoize func
cache = {}
proc do |*args|
next cache[args] if cache[args]
cache[args] = func[*args]
end
end
def slow_double x
sleep 2
x * 2
end
memoized_double = memoize(method :slow_double)
memoized_double[4] # takes 2 seconds
memoized_double[4] # returns instantly