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.
@@ -0,0 +1,3 @@
1
+ == 0.1.0 / 2007-05-25
2
+
3
+ * First release memoized package.
@@ -0,0 +1,7 @@
1
+ History.txt
2
+ Manifest.txt
3
+ README.txt
4
+ Rakefile
5
+ bin/memoize
6
+ lib/memoize.rb
7
+ test/test_memoize.rb
@@ -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.
@@ -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
File without changes
@@ -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: