Memoize 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/History.txt +3 -0
- data/Manifest.txt +7 -0
- data/README.txt +48 -0
- data/Rakefile +15 -0
- data/bin/memoize +0 -0
- data/lib/memoize.rb +302 -0
- data/test/test_memoize.rb +37 -0
- metadata +59 -0
data/History.txt
ADDED
data/Manifest.txt
ADDED
data/README.txt
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
Memoize
|
2
|
+
by Rakuto Furutani <rakuto@gmail.com>
|
3
|
+
Blog: http://rakuto.blogspot.com/
|
4
|
+
|
5
|
+
== DESCRIPTION:
|
6
|
+
|
7
|
+
Memoize is implementation Memoization for Ruby, this techinique to make functions faster.
|
8
|
+
This memoization library support own storage to save memoized data.
|
9
|
+
|
10
|
+
== FEATURES/PROBLEMS:
|
11
|
+
|
12
|
+
* Memoization - http://en.wikipedia.org/wiki/Memoization
|
13
|
+
|
14
|
+
== SYNOPSIS:
|
15
|
+
|
16
|
+
|
17
|
+
== REQUIREMENTS:
|
18
|
+
|
19
|
+
None
|
20
|
+
|
21
|
+
== INSTALL:
|
22
|
+
|
23
|
+
% sudo gem install memoize
|
24
|
+
|
25
|
+
== LICENSE:
|
26
|
+
|
27
|
+
MIT License
|
28
|
+
|
29
|
+
Copyright (c) 2007 Rakuto Furutani
|
30
|
+
|
31
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
32
|
+
a copy of this software and associated documentation files (the
|
33
|
+
'Software'), to deal in the Software without restriction, including
|
34
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
35
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
36
|
+
permit persons to whom the Software is furnished to do so, subject to
|
37
|
+
the following conditions:
|
38
|
+
|
39
|
+
The above copyright notice and this permission notice shall be
|
40
|
+
included in all copies or substantial portions of the Software.
|
41
|
+
|
42
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
43
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
44
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
45
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
46
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
47
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
48
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
# -*- ruby -*-
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'hoe'
|
5
|
+
require './lib/memoize.rb'
|
6
|
+
|
7
|
+
Hoe.new('Memoize', Memoize::VERSION) do |p|
|
8
|
+
p.rubyforge_name = 'memoize'
|
9
|
+
p.summary = 'Make functions faster by Memoization techniques'
|
10
|
+
p.description = p.paragraphs_of('README.txt', 2..5).join("\n\n")
|
11
|
+
p.url = p.paragraphs_of('README.txt', 0).first.split(/\n/)[1..-1]
|
12
|
+
p.changes = p.paragraphs_of('History.txt', 0..1).join("\n\n")
|
13
|
+
end
|
14
|
+
|
15
|
+
# vim: syntax=Ruby
|
data/bin/memoize
ADDED
File without changes
|
data/lib/memoize.rb
ADDED
@@ -0,0 +1,302 @@
|
|
1
|
+
# Memoize is implementation Memoization for Ruby, this techinique to make functions faster.
|
2
|
+
#
|
3
|
+
# == Caveats:
|
4
|
+
# * Do not memoize a function whose behaviou depends on program state.
|
5
|
+
# * Do not memoize a function with side effects.
|
6
|
+
# * Do not memoize a function that returns a data structure that is modified by it's caller.
|
7
|
+
#
|
8
|
+
# == See:
|
9
|
+
# Memoization - http://en.wikipedia.org/wiki/Memoization
|
10
|
+
#
|
11
|
+
module Memoize
|
12
|
+
VERSION = '0.1.0'
|
13
|
+
@@stores = {}
|
14
|
+
|
15
|
+
# Memoize::Storable class is abstract class. You must implement class which memoization data is saved.
|
16
|
+
# This module offer two storage class.
|
17
|
+
#
|
18
|
+
# * Memoize::MemoryStore -
|
19
|
+
# * Memoize::PStore - Persisten cache support.
|
20
|
+
#
|
21
|
+
class Storable
|
22
|
+
# {{{
|
23
|
+
attr_accessor :store
|
24
|
+
|
25
|
+
def initialize(name)
|
26
|
+
raise "Memoize::Strable don't instantiage for abstract class."
|
27
|
+
end
|
28
|
+
|
29
|
+
def set(key, value)
|
30
|
+
end
|
31
|
+
|
32
|
+
def get(key)
|
33
|
+
end
|
34
|
+
|
35
|
+
def update(key)
|
36
|
+
end
|
37
|
+
|
38
|
+
def delete(key)
|
39
|
+
end
|
40
|
+
|
41
|
+
def delete_all
|
42
|
+
end
|
43
|
+
# }}}
|
44
|
+
end
|
45
|
+
|
46
|
+
# MemoryStore is class which store memoization data to memory.
|
47
|
+
class MemoryStore < Memoize::Storable
|
48
|
+
# {{{
|
49
|
+
attr_accessor :store
|
50
|
+
|
51
|
+
def initialize
|
52
|
+
@store = {}
|
53
|
+
end
|
54
|
+
|
55
|
+
def get(key)
|
56
|
+
@store[key]
|
57
|
+
end
|
58
|
+
|
59
|
+
def set(key, value)
|
60
|
+
@store[key] = value
|
61
|
+
end
|
62
|
+
|
63
|
+
def delete(key)
|
64
|
+
@store.delete(key)
|
65
|
+
end
|
66
|
+
|
67
|
+
def delete_all
|
68
|
+
@store = {}
|
69
|
+
end
|
70
|
+
# }}}
|
71
|
+
end
|
72
|
+
|
73
|
+
# PStore is class which store memoization data with local file system.
|
74
|
+
# This store class be able to use persistent cache, because this class write out
|
75
|
+
# cache data on file when process is exit.
|
76
|
+
class PStore < Memoize::MemoryStore
|
77
|
+
# {{{
|
78
|
+
attr_accessor :store
|
79
|
+
PREFIX_SAVE_FILE_DIR = "/tmp"
|
80
|
+
SUFFIX_SAVE_FILE_EXT = ".cache"
|
81
|
+
|
82
|
+
def initialize(name)
|
83
|
+
@name = name
|
84
|
+
filename = encoded_filename(@name)
|
85
|
+
begin
|
86
|
+
if File.exists?(filename)
|
87
|
+
File.open(filename, "rb") do |io|
|
88
|
+
@store = Marshal.load(io)
|
89
|
+
end
|
90
|
+
else
|
91
|
+
@store = {}
|
92
|
+
end
|
93
|
+
rescue
|
94
|
+
@store = {}
|
95
|
+
end
|
96
|
+
# Write out cache
|
97
|
+
at_exit {
|
98
|
+
File.open("#{filename}", "wb+") do |io|
|
99
|
+
io.flock(File::LOCK_EX)
|
100
|
+
Marshal.dump(@store, io)
|
101
|
+
io.flock(File::LOCK_UN)
|
102
|
+
end
|
103
|
+
}
|
104
|
+
end
|
105
|
+
|
106
|
+
# Delete all memoized data and persisten cache fille
|
107
|
+
def delete_all
|
108
|
+
filename = encoded_filename(@name)
|
109
|
+
File.exists?(filename) && File.unlink(filename)
|
110
|
+
@store = {}
|
111
|
+
end
|
112
|
+
|
113
|
+
private
|
114
|
+
# Get the encoded file name
|
115
|
+
def encoded_filename(name)
|
116
|
+
File.join(PREFIX_SAVE_FILE_DIR, [name].pack('m').chomp+SUFFIX_SAVE_FILE_EXT)
|
117
|
+
end
|
118
|
+
# }}}
|
119
|
+
end
|
120
|
+
|
121
|
+
module_function
|
122
|
+
# Make memoized function.
|
123
|
+
#
|
124
|
+
# Parameters:
|
125
|
+
# * klass - You must surely specify 'self'. This parameter used to overwrite Toplevell function and
|
126
|
+
# register as toplevel function. I need to think better the means ;)
|
127
|
+
# * name - an memorized function name.
|
128
|
+
# * options - an specify options. <tt>:as</tt> is memorized function name, default is the same second arguments value.
|
129
|
+
# <tt>:store</tt> is specify class which store memorized data.
|
130
|
+
#
|
131
|
+
# == Memoized function examples
|
132
|
+
# def fib(n)
|
133
|
+
# return 1 if n < 2
|
134
|
+
# fib(n-1) + fib(n-2)
|
135
|
+
# end
|
136
|
+
#
|
137
|
+
# end
|
138
|
+
# Memoize.register(self, 'fib')
|
139
|
+
# fib(30) # => 1346269 fast
|
140
|
+
# fib(30) # => 1346269 very fast
|
141
|
+
#
|
142
|
+
# # Unmemorize
|
143
|
+
# Memoize.unmemoize(self, 'fib')
|
144
|
+
# fib(30) # => very slow
|
145
|
+
#
|
146
|
+
# == Memoized one Object methods
|
147
|
+
# class Math2
|
148
|
+
# def self.fib(N)
|
149
|
+
# return 1 if n < 2
|
150
|
+
# fib(n-1) + fib(n-2)
|
151
|
+
# end
|
152
|
+
# end
|
153
|
+
#
|
154
|
+
# Memoize.memorize(self, 'Math2.fib')
|
155
|
+
# Math.fib(30) # => fast
|
156
|
+
# Math.fib(30) # => very fast
|
157
|
+
# Memoize.unmemoize(self, 'Math2.fib')
|
158
|
+
#
|
159
|
+
# == Custom storage
|
160
|
+
# You can use own memoized data storage, in doing so you must implementaion following method
|
161
|
+
#
|
162
|
+
# Method/Arity:
|
163
|
+
# * initialize/1 - this is constructor
|
164
|
+
# * get/1 - an get the cache data with key
|
165
|
+
# * set/2 - an set the cache data with key and value
|
166
|
+
# * delete/1 - an delete the cache data with key
|
167
|
+
# * delete_all/0 - an delete all cache
|
168
|
+
#
|
169
|
+
# Following class is MemCache storage sample.
|
170
|
+
#
|
171
|
+
# # Memoized data storage using MemCache
|
172
|
+
# require 'memcache'
|
173
|
+
#
|
174
|
+
# class MemCacheStore < Memoize::Storable
|
175
|
+
# attr_accessor :cache, :keys
|
176
|
+
# def initialize(name)
|
177
|
+
# @keys = []
|
178
|
+
# @cache = MemCache.new 'localhost:11211', :namespace => 'rakuto.blogspot.com'
|
179
|
+
# end
|
180
|
+
#
|
181
|
+
# def get(key)
|
182
|
+
# @cache.get(key)
|
183
|
+
# end
|
184
|
+
#
|
185
|
+
# def set(key, value)
|
186
|
+
# @keys << key unless @keys.include?(key)
|
187
|
+
# @cache.set(key, value)
|
188
|
+
# end
|
189
|
+
#
|
190
|
+
# def delete(key)
|
191
|
+
# @cache.delete(key)
|
192
|
+
# end
|
193
|
+
#
|
194
|
+
# def delete_all
|
195
|
+
# @keys.each { |key| @cache.delete(key) }
|
196
|
+
# end
|
197
|
+
# end
|
198
|
+
#
|
199
|
+
# Memoize.register(self, 'fib', :store => MemCacheStore)
|
200
|
+
# fib(30) # => fast
|
201
|
+
# fib(30) # => fast
|
202
|
+
#
|
203
|
+
def register(klass, name, options={})
|
204
|
+
ns = name.split(/::|\./)
|
205
|
+
method, klass = ns.pop, (ns.empty? ? klass : Object.const_get(ns.join("::")))
|
206
|
+
store = options[:store].nil? ? PStore.new(name) : options[:store].new(name)
|
207
|
+
as_method = options[:as] || method
|
208
|
+
Memoize._set_store(name, store)
|
209
|
+
(class<<klass;self;end).instance_eval do # for Ruby1.9
|
210
|
+
method_name = "#{as_method}_without_memoize"
|
211
|
+
memoized_method_name = "#{as_method}_with_memoize"
|
212
|
+
define_method(memoized_method_name) do |*args|
|
213
|
+
store = Memoize._get_store(name)
|
214
|
+
ret = store.get(args)
|
215
|
+
if ret.nil?
|
216
|
+
ret = send(method_name, *args)
|
217
|
+
store.set(args, ret)
|
218
|
+
end
|
219
|
+
ret
|
220
|
+
end
|
221
|
+
alias_method method_name, method
|
222
|
+
alias_method as_method, memoized_method_name
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
# Specify unmemoize method.
|
227
|
+
# If you delete persistent cache when set delete_all true.
|
228
|
+
#
|
229
|
+
# == Example
|
230
|
+
# Memoize.memoize(self, 'slow_func')
|
231
|
+
# slow_func(arg)
|
232
|
+
# Memoize.unmemoize(self, 'slow_func')
|
233
|
+
#
|
234
|
+
def unmemoize(klass, name, delete_all=false)
|
235
|
+
(class<<klass;self;end).class_eval do
|
236
|
+
undef_method("#{name}_with_memoize")
|
237
|
+
alias_method name, "#{name}_without_memoize"
|
238
|
+
end
|
239
|
+
delete_all && @store.delete_all
|
240
|
+
end
|
241
|
+
|
242
|
+
# This method is public, but you don't need call directly.
|
243
|
+
def _set_store(name, store)
|
244
|
+
@@stores[name] = store
|
245
|
+
end
|
246
|
+
|
247
|
+
# This method is public, but you don't need call directly.
|
248
|
+
def _get_store(name)
|
249
|
+
@@stores[name]
|
250
|
+
end
|
251
|
+
|
252
|
+
# Not implemented
|
253
|
+
module Expire
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
257
|
+
if $0 == __FILE__
|
258
|
+
def fib(n)
|
259
|
+
return 1 if n < 2
|
260
|
+
fib(n-1) + fib(n-2)
|
261
|
+
end
|
262
|
+
|
263
|
+
class Math2
|
264
|
+
def self.fib(n)
|
265
|
+
return 1 if n < 2
|
266
|
+
fib(n-1) + fib(n-2)
|
267
|
+
end
|
268
|
+
end
|
269
|
+
|
270
|
+
class MemCacheStore < Memoize::Storable
|
271
|
+
attr_accessor :cache, :keys
|
272
|
+
def initialize(name)
|
273
|
+
@keys = []
|
274
|
+
@cache = MemCache.new 'localhost:11211', :namespace => 'rakuto.blogspot.com'
|
275
|
+
end
|
276
|
+
|
277
|
+
def get(key)
|
278
|
+
@cache.get(key)
|
279
|
+
end
|
280
|
+
|
281
|
+
def set(key, value)
|
282
|
+
@keys << key unless @keys.include?(key)
|
283
|
+
@cache.set(key, value)
|
284
|
+
end
|
285
|
+
|
286
|
+
def delete(key)
|
287
|
+
@cache.delete(key)
|
288
|
+
end
|
289
|
+
|
290
|
+
def delete_all
|
291
|
+
@keys.each { |key| @cache.delete(key) }
|
292
|
+
end
|
293
|
+
end
|
294
|
+
|
295
|
+
|
296
|
+
Memoize.register(self, 'Math2.fib', :store => MemCacheStore)
|
297
|
+
Benchmark.bm do |b|
|
298
|
+
b.report("fib(30) with memoize: ") { Math2.fib(30) }
|
299
|
+
#Memoize.unmemoize(self, 'fib')
|
300
|
+
b.report("fib(30)") { Math2.fib(30) }
|
301
|
+
end
|
302
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
$: << '../lib/'
|
2
|
+
require 'memoize'
|
3
|
+
require 'benchmark'
|
4
|
+
|
5
|
+
def fib(n)
|
6
|
+
return 1 if n < 2
|
7
|
+
fib(n-1) + fib(n-2)
|
8
|
+
end
|
9
|
+
|
10
|
+
class Math2
|
11
|
+
def self.fib(n)
|
12
|
+
return 1 if n < 2
|
13
|
+
fib(n-1) + fib(n-2)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
Benchmark.bm do |b|
|
18
|
+
b.report("fib(30): ") { fib(30) }
|
19
|
+
Memoize.register(self, 'fib')
|
20
|
+
b.report("fib(30) with memoize") { fib(30) }
|
21
|
+
b.report("fib(30) with memoize") { fib(30) }
|
22
|
+
Memoize.unmemoize(self, 'fib')
|
23
|
+
end
|
24
|
+
|
25
|
+
Benchmark.bm do |b|
|
26
|
+
Memoize.register(self, 'fib', :as => 'fastfib')
|
27
|
+
b.report("fastfib(30) with memoize") { fastfib(30) }
|
28
|
+
b.report("fastfib(30) with memoize") { fastfib(30) }
|
29
|
+
Memoize.unmemoize(self, 'fastfib')
|
30
|
+
end
|
31
|
+
|
32
|
+
Benchmark.bm do |b|
|
33
|
+
b.report("fib(30): ") { Math2.fib(30) }
|
34
|
+
Memoize.register(self, 'Math2.fib')
|
35
|
+
b.report("fib(30) with memoize") { Math2.fib(30) }
|
36
|
+
b.report("fib(30) with memoize") { Math2.fib(30) }
|
37
|
+
end
|
metadata
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
rubygems_version: 0.8.11
|
3
|
+
specification_version: 1
|
4
|
+
name: Memoize
|
5
|
+
version: !ruby/object:Gem::Version
|
6
|
+
version: 0.1.0
|
7
|
+
date: 2007-05-24 00:00:00 +00:00
|
8
|
+
summary: Make functions faster by Memoization techniques
|
9
|
+
require_paths:
|
10
|
+
- lib
|
11
|
+
email: ryand-ruby@zenspider.com
|
12
|
+
homepage: " by Rakuto Furutani <rakuto@gmail.com>"
|
13
|
+
rubyforge_project: memoize
|
14
|
+
description: "== FEATURES/PROBLEMS: * Memoization - http://en.wikipedia.org/wiki/Memoization == SYNOPSIS: == REQUIREMENTS: None"
|
15
|
+
autorequire:
|
16
|
+
default_executable:
|
17
|
+
bindir: bin
|
18
|
+
has_rdoc: true
|
19
|
+
required_ruby_version: !ruby/object:Gem::Version::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">"
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 0.0.0
|
24
|
+
version:
|
25
|
+
platform: ruby
|
26
|
+
signing_key:
|
27
|
+
cert_chain:
|
28
|
+
authors:
|
29
|
+
- Ryan Davis
|
30
|
+
files:
|
31
|
+
- History.txt
|
32
|
+
- Manifest.txt
|
33
|
+
- README.txt
|
34
|
+
- Rakefile
|
35
|
+
- bin/memoize
|
36
|
+
- lib/memoize.rb
|
37
|
+
- test/test_memoize.rb
|
38
|
+
test_files:
|
39
|
+
- test/test_memoize.rb
|
40
|
+
rdoc_options: []
|
41
|
+
|
42
|
+
extra_rdoc_files: []
|
43
|
+
|
44
|
+
executables:
|
45
|
+
- memoize
|
46
|
+
extensions: []
|
47
|
+
|
48
|
+
requirements: []
|
49
|
+
|
50
|
+
dependencies:
|
51
|
+
- !ruby/object:Gem::Dependency
|
52
|
+
name: hoe
|
53
|
+
version_requirement:
|
54
|
+
version_requirements: !ruby/object:Gem::Version::Requirement
|
55
|
+
requirements:
|
56
|
+
- - ">="
|
57
|
+
- !ruby/object:Gem::Version
|
58
|
+
version: 1.2.0
|
59
|
+
version:
|