2016-01-05 28 views
3

我可以memoize的Ruby的功能,通過使用本地範圍和封閉:你可以處理Ruby的函數式編程方法嗎,比如記憶一個函數嗎?

require "benchmark" 

fib = ->(n) { 
    return 0 if n < 0 
    return 1 if n == 0 
    return fib.(n-1) + fib.(n-2) 
} 

memoize = ->(f) { 
    already_know = {} 

    new_function = ->(n) { 
    already_know[n] ||= f.(n) 
    return already_know[n] 
    } 
    return new_function 
} 

fib = memoize.(fib) 

puts Benchmark.measure { p fib.(42) } 

,並花了0.000011秒運行。如果沒有fib = memoize.(fib)行,則需要259秒才能運行。

但你可以用Ruby中的方法(而不是函數)做同樣的事情嗎?看起來像一個Python方法更像一個函數,因爲你可以很容易地用Python方法做到這一點,而Ruby的方法不像一個函數 - 也許是因爲Ruby中的方法不是一個對象。但問題是,你可以在上面的代碼中執行類似memoize的操作,以使方法變爲記憶嗎?

+1

結帳[4種簡單記憶化模式在紅寶石(http://www.justinweiss.com/articles/4-simple-memoization-patterns -in-ruby-and-one-gem /)使用'@ instance_variable' –

+0

我很困惑。你顯示Ruby(?)代碼來做一件事,然後問如何在Ruby中做到這一點? –

+0

@JonathonReinhart它是函數與方法 –

回答

2

您可以別名原始方法,並添加一個簡單的緩存到原始實現。

class Fib 
    def fib(n) 
    case 
    when n < 0 then 0 
    when n == 0 then 1 
    else fib(n-1) + fib(n-2) 
    end 
    end 
end 
puts Benchmark.measure { p Fib.new.fib(35) } 

class Fib 
    alias_method :fib_ori, :fib 

    def fib(n) 
    (@fib_cache ||= {})[n] ||= fib_ori(n) 
    end 
end 
puts Benchmark.measure { p Fib.new.fib(35) } 
+1

看起來像它的工作...不像函數式編程...使用實例變量而不是範圍和閉包,但它的工作原理。如果有一種解決方案不需要引入額外的實例變量... –

1

類似於sschmeck's solution,但通過繼承:類似於CLOS或

class Fibonacci 
    def at(n) 
    return 0 if n < 0 
    return 1 if n == 0 
    return at(n - 1) + at(n - 2) 
    end 
end 

class MemoizedFibonacci < Fibonacci 
    def initialize 
    @memo = {} 
    end 
    def at(n) 
    @memo[n] ||= super 
    end 
end 
+1

'at'方法中的最後一個'return'看起來多餘:) –

+0

@AndreyDeineko我剛剛複製了OP的代碼 – Stefan

+1

@AndreyDeineko顯式'即使在語法上不必要時,「返回」有時也用於提高可讀性。 – naomik

1
Module#prepend

在紅寶石2+的溶液中加入特別是在因爲它可以(除了別的以外)作爲一個方法組合子/裝飾蟒蛇。這樣,你實際上不需要訪問方法本身,你可以重寫它。

class Module 
    def memoize(meth) 
    prepend(Module.new do 
     memo = {} 

     define_method(meth) do |*args, &blk| 
     memo[[self, *args, blk]] ||= super(*args, &blk) 
     end 
    end) 
    end 
end 

class Integer 
    memoize def fib 
    raise ArgumentError if self < 0 
    return self if self < 2 
    pred.fib + pred.pred.fib 
    end 
end 

require 'benchmark' 

puts Benchmark.measure { p 42.fib } 

在舊版本的紅寶石(1.9或以上),你將不得不做這樣的事情:

class Module 
    def memoize(meth) 
    memo = {} 
    old_meth = instance_method(meth) 

    define_method(meth) do |*args, &blk| 
     memo[[self, *args, blk]] ||= old_meth.bind(self).(*args, &blk) 
    end 
    end 
end 

此外,def評估到Symbol表示方法的名字被定義爲在Ruby中2.2增加,因此,在舊版本中,你不得不這樣做,而不是:

class Integer 
    def fib 
    raise ArgumentError if self < 0 
    return self if self < 2 
    pred.fib + pred.pred.fib 
    end 
    memoize :fib 
end 

我們可以作爲一個耙用於其使用這種伎倆方法,雖然,以使其memoize的下一個方法被定義:

class Module 
    def memoize(meth=nil) 
    return @__memoize_next_method__ = true unless meth 
    memo = {} 
    old_meth = instance_method(meth) 

    define_method(meth) do |*args, &blk| 
     memo[[self, *args, blk]] ||= old_meth.bind(self).(*args, &blk) 
    end 
    end 

    def method_added(meth) 
    return if @__recursing__ 
    @__recursing__ = true # protect against infinite recursion 

    if @__memoize_next_method__ 
     memoize(meth) 
     @__memoize_next_method__ = nil 
    end 

    @recursing = nil 
    end 
end 

class Integer 
    memoize 
    def fib 
    raise ArgumentError if self < 0 
    return self if self < 2 
    pred.fib + pred.pred.fib 
    end 
end 
相關問題